From 0077c3eec63ed7e5cf668d543c751b8f117e0941 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 20 Nov 2015 17:50:05 +0200 Subject: [PATCH] Implement horizontal and vertical alignments for stack layout --- AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 24 +++++ AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 17 +++- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 52 +++++++++- .../Private/ASStackLayoutSpecUtilities.h | 60 ++++++++++++ .../ASStackLayoutSpecSnapshotTests.mm | 90 +++++++++++++++++- ...calAlignments_horizontalBottomRight@2x.png | Bin 0 -> 4455 bytes ...VerticalAlignments_horizontalCenter@2x.png | Bin 0 -> 4812 bytes ...erticalAlignments_horizontalTopLeft@2x.png | Bin 0 -> 4728 bytes ...ticalAlignments_verticalBottomRight@2x.png | Bin 0 -> 4497 bytes ...ndVerticalAlignments_verticalCenter@2x.png | Bin 0 -> 5596 bytes ...dVerticalAlignments_verticalTopLeft@2x.png | Bin 0 -> 4289 bytes 11 files changed, 235 insertions(+), 8 deletions(-) create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@2x.png create mode 100644 AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 0ca7bb3c49..47d72b34d6 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -67,3 +67,27 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { /** Expand to fill cross axis */ ASStackLayoutAlignSelfStretch, }; + +/** Orientation of children along horizontal axis */ +typedef NS_ENUM(NSUInteger, ASHorizontalAlignment) { + /** No alignment specified. Default value */ + ASHorizontalAlignmentNone, + /** Left aligned */ + ASAlignmentLeft, + /** Center aligned */ + ASAlignmentMiddle, + /** Right aligned */ + ASAlignmentRight, +}; + +/** Orientation of children along vertical axis */ +typedef NS_ENUM(NSUInteger, ASVerticalAlignment) { + /** No alignment specified. Default value */ + ASVerticalAlignmentNone, + /** Top aligned */ + ASAlignmentTop, + /** Center aligned */ + ASAlignmentCenter, + /** Bottom aligned */ + ASAlignmentBottom, +}; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index ebf7130bfe..a2afc29609 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -35,10 +35,25 @@ */ @interface ASStackLayoutSpec : ASLayoutSpec -/** Specifies the direction children are stacked in. */ +/** + Specifies the direction children are stacked in. If horizontalAlignment and verticalAlignment were set, + they will be resolved again, causing justifyContent and alignItems to be updated accordingly + */ @property (nonatomic, assign) ASStackLayoutDirection direction; /** The amount of space between each child. */ @property (nonatomic, assign) CGFloat spacing; +/** + Specifies how children are aligned horizontally. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties + */ +@property (nonatomic, assign) ASHorizontalAlignment horizontalAlignment; +/** + Specifies how children are aligned vertically. Depends on the stack direction, setting the alignment causes either + justifyContent or alignItems to be updated. The alignment will remain valid after future direction changes. + Thus, it is preferred to those properties + */ +@property (nonatomic, assign) ASVerticalAlignment verticalAlignment; /** The amount of space between each child. */ @property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 0763bf2615..ed07b7d6e0 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -25,7 +25,7 @@ @implementation ASStackLayoutSpec { - ASDN::RecursiveMutex _propertyLock; + ASDN::RecursiveMutex _propertyLock; } - (instancetype)init @@ -58,8 +58,10 @@ return nil; } _direction = direction; - _alignItems = alignItems; _spacing = spacing; + _horizontalAlignment = ASHorizontalAlignmentNone; + _verticalAlignment = ASVerticalAlignmentNone; + _alignItems = alignItems; _justifyContent = justifyContent; [self setChildren:children]; @@ -69,18 +71,44 @@ - (void)setDirection:(ASStackLayoutDirection)direction { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _direction = direction; + if (_direction != direction) { + _direction = direction; + [self resolveHorizontalAlignment]; + [self resolveVerticalAlignment]; + } +} + +- (void)setHorizontalAlignment:(ASHorizontalAlignment)horizontalAlignment +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + if (_horizontalAlignment != horizontalAlignment) { + _horizontalAlignment = horizontalAlignment; + [self resolveHorizontalAlignment]; + } +} + +- (void)setVerticalAlignment:(ASVerticalAlignment)verticalAlignment +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + if (_verticalAlignment != verticalAlignment) { + _verticalAlignment = verticalAlignment; + [self resolveVerticalAlignment]; + } } - (void)setAlignItems:(ASStackLayoutAlignItems)alignItems { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used"); + ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used"); _alignItems = alignItems; } - (void)setJustifyContent:(ASStackLayoutJustifyContent)justifyContent { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + ASDisplayNodeAssert(_horizontalAlignment == ASHorizontalAlignmentNone, @"Cannot set this property directly because horizontalAlignment is being used"); + ASDisplayNodeAssert(_verticalAlignment == ASVerticalAlignmentNone, @"Cannot set this property directly because verticalAlignment is being used"); _justifyContent = justifyContent; } @@ -149,6 +177,24 @@ sublayouts:sublayouts]; } +- (void)resolveHorizontalAlignment +{ + if (_direction == ASStackLayoutDirectionHorizontal) { + _justifyContent = justifyContent(_horizontalAlignment, _justifyContent); + } else { + _alignItems = alignment(_horizontalAlignment, _alignItems); + } +} + +- (void)resolveVerticalAlignment +{ + if (_direction == ASStackLayoutDirectionHorizontal) { + _alignItems = alignment(_verticalAlignment, _alignItems); + } else { + _justifyContent = justifyContent(_verticalAlignment, _justifyContent); + } +} + @end @implementation ASStackLayoutSpec (Debugging) diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index a61218cfe4..ae018a1f87 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -68,3 +68,63 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment, return stackAlignment; } } + +inline ASStackLayoutAlignItems alignment(ASHorizontalAlignment alignment, ASStackLayoutAlignItems defaultAlignment) +{ + switch (alignment) { + case ASAlignmentLeft: + return ASStackLayoutAlignItemsStart; + case ASAlignmentMiddle: + return ASStackLayoutAlignItemsCenter; + case ASAlignmentRight: + return ASStackLayoutAlignItemsEnd; + case ASHorizontalAlignmentNone: + default: + return defaultAlignment; + } +} + +inline ASStackLayoutAlignItems alignment(ASVerticalAlignment alignment, ASStackLayoutAlignItems defaultAlignment) +{ + switch (alignment) { + case ASAlignmentTop: + return ASStackLayoutAlignItemsStart; + case ASAlignmentCenter: + return ASStackLayoutAlignItemsCenter; + case ASAlignmentBottom: + return ASStackLayoutAlignItemsEnd; + case ASVerticalAlignmentNone: + default: + return defaultAlignment; + } +} + +inline ASStackLayoutJustifyContent justifyContent(ASHorizontalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent) +{ + switch (alignment) { + case ASAlignmentLeft: + return ASStackLayoutJustifyContentStart; + case ASAlignmentMiddle: + return ASStackLayoutJustifyContentCenter; + case ASAlignmentRight: + return ASStackLayoutJustifyContentEnd; + case ASHorizontalAlignmentNone: + default: + return defaultJustifyContent; + } +} + +inline ASStackLayoutJustifyContent justifyContent(ASVerticalAlignment alignment, ASStackLayoutJustifyContent defaultJustifyContent) +{ + switch (alignment) { + case ASAlignmentTop: + return ASStackLayoutJustifyContentStart; + case ASAlignmentCenter: + return ASStackLayoutJustifyContentCenter; + case ASAlignmentBottom: + return ASStackLayoutJustifyContentEnd; + case ASVerticalAlignmentNone: + default: + return defaultJustifyContent; + } +} diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 2252078da6..5c3733e707 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -28,6 +28,8 @@ self.recordMode = NO; } +#pragma mark - Utility methods + static NSArray *defaultSubnodes() { return defaultSubnodesWithSameSize(CGSizeZero, NO); @@ -63,6 +65,24 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) [self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier]; } +- (void)testStackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + itemsHorizontalAlignment:(ASHorizontalAlignment)horizontalAlignment + itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment + identifier:(NSString *)identifier +{ + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); + + ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; + stackLayoutSpec.direction = direction; + stackLayoutSpec.children = subnodes; + [stackLayoutSpec setHorizontalAlignment:horizontalAlignment]; + [stackLayoutSpec setVerticalAlignment:verticalAlignment]; + + CGSize exactSize = CGSizeMake(200, 200); + static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize); + [self testStackLayoutSpec:stackLayoutSpec sizeRange:kSize subnodes:subnodes identifier:identifier]; +} + - (void)testStackLayoutSpecWithStyle:(ASStackLayoutSpecStyle)style sizeRange:(ASSizeRange)sizeRange subnodes:(NSArray *)subnodes @@ -76,13 +96,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) sizeRange:(ASSizeRange)sizeRange subnodes:(NSArray *)subnodes identifier:(NSString *)identifier +{ + ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction + spacing:style.spacing + justifyContent:style.justifyContent + alignItems:style.alignItems + children:children]; + [self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; +} + +- (void)testStackLayoutSpec:(ASStackLayoutSpec *)stackLayoutSpec + sizeRange:(ASSizeRange)sizeRange + subnodes:(NSArray *)subnodes + identifier:(NSString *)identifier { ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]); - ASLayoutSpec *layoutSpec = - [ASBackgroundLayoutSpec - backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction spacing:style.spacing justifyContent:style.justifyContent alignItems:style.alignItems children:children] - background:backgroundNode]; + ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode]; NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode]; [newSubnodes addObjectsFromArray:subnodes]; @@ -90,6 +120,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:newSubnodes identifier:identifier]; } +#pragma mark - + - (void)testUnderflowBehaviors { // width 300px; height 0-300px @@ -522,4 +554,54 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; } +- (void)testHorizontalAndVerticalAlignments +{ + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentLeft itemsVerticalAlignment:ASAlignmentTop identifier:@"horizontalTopLeft"]; + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentMiddle itemsVerticalAlignment:ASAlignmentCenter identifier:@"horizontalCenter"]; + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASAlignmentRight itemsVerticalAlignment:ASAlignmentBottom identifier:@"horizontalBottomRight"]; + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentLeft itemsVerticalAlignment:ASAlignmentTop identifier:@"verticalTopLeft"]; + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentMiddle itemsVerticalAlignment:ASAlignmentCenter identifier:@"verticalCenter"]; + [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionVertical itemsHorizontalAlignment:ASAlignmentRight itemsVerticalAlignment:ASAlignmentBottom identifier:@"verticalBottomRight"]; +} + +- (void)testDirectionChangeAfterSettingHorizontalAndVerticalAlignments +{ + ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; // Default direction is horizontal + stackLayoutSpec.horizontalAlignment = ASAlignmentRight; + stackLayoutSpec.verticalAlignment = ASAlignmentCenter; + XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsCenter); + XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentEnd); + + stackLayoutSpec.direction = ASStackLayoutDirectionVertical; + XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsEnd); + XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentCenter); +} + +- (void)testAlignItemsAndJustifyContentRestrictionsIfHorizontalAndVerticalAlignmentsAreUsed +{ + ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; + + // No assertions should be thrown here because alignments are not used + stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd; + stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd; + + // Set alignments and assert that assertions are thrown + stackLayoutSpec.horizontalAlignment = ASAlignmentMiddle; + stackLayoutSpec.verticalAlignment = ASAlignmentCenter; + XCTAssertThrows(stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd); + XCTAssertThrows(stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd); + + // Unset alignments. alignItems and justifyContent should not be changed + stackLayoutSpec.horizontalAlignment = ASHorizontalAlignmentNone; + stackLayoutSpec.verticalAlignment = ASVerticalAlignmentNone; + XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsCenter); + XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentCenter); + + // Now that alignments are none, setting alignItems and justifyContent should be allowed again + stackLayoutSpec.alignItems = ASStackLayoutAlignItemsEnd; + stackLayoutSpec.justifyContent = ASStackLayoutJustifyContentEnd; + XCTAssertEqual(stackLayoutSpec.alignItems, ASStackLayoutAlignItemsEnd); + XCTAssertEqual(stackLayoutSpec.justifyContent, ASStackLayoutJustifyContentEnd); +} + @end diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..489764fe6e29e2f920e2cae42680346d205addee GIT binary patch literal 4455 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~hKkh>GZx^prwfgH{PkH}&M z1~F9-X7mhF`2ZA@$#n4#0n$uB3<4*BSOZD}+3a4HPqU@~X)#Y1$B+ufH&-|EGB9u) zHuzV+>(7LnZ(?v2qV&M?rU=lY|1JVi(2~3YTL9_xVhewjJl0yRnBjZPAkkkYg z7L|>Xf(i}|4GllQx;&VeCf(=(sdjMq!vGReVPy2oaRaGVP^bfHWn!7s(4b1FYK{(hplJ+^DhdjnXFEWuS&r}nO>1xv5}0r#%>|^I=@I*=b4EjF zH0_M$tkH63v{a^ICGmiff#LuEnNOES0BhGnpc?Y+b;qou1`N)Q_y1oy_x}l7l+<^z)FES$g<+d9UN9H5#hLC8Qsc(yaB&P#CE1FF^pX0Y&_IPy#Y z#Fh}q2dh?NWH#Dp39ixDj!1)RJp+ zBzG!{y05dVZC&wZTEj6f(`gn#g%T6yIIOn2>z2G!%3EfoMbX!n+%BoxSxmxKZ9Fou z>u|#pmZ+~U&0uD)_K#Q4+O4`^V^FVZUx*Bwiu#3B{@X7?Z0d`t{lye`QClP{g$MSBW&#rW03}>kCZ1i^t22qh213<n8@slU@r>M=k~psW5oD`njxgN@xNA3q(B! literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eb0ea8c3dae6ddb3f4683a4cfce5efc6b629f3e8 GIT binary patch literal 4812 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~hKkh>GZx^prwfgH{PkH}&M z1~F9-X7mhF`2ZA@$#n4#0n$uB3<4*BSOZD}*&Mh0ik`{?>1CcSjv*C{Z*DlQQgIhy zIk@Zp($&&ccct4KR17)S9?@2rJ*S}7cD+4^fPzDVgFwZhUSTE{Am;;Dhr2@q10$2C zgM-j)WssPFKt4!-Wl}?fikUk|k%GcKkN}4YBco?dAIJy?hdm$x0S_joNjGE!fo3)| zR4{|oPGDhC*=VT*GL!Km8%V7JCx=H;u?xsdrboPB`-KE19C_9OGLz+qG*B}`ql$t; z=UG9JnH(MFqYiQy4WZFAG@8o>dpT6#?=bKE{;vIfh9A5O&*&e0zy0X%AD3bkac56hPW#6x-AM7{q30vgay|(Y4@1S!x<>dyyh}Zlr?Pr(IcX*XBzf2uw z1yhkHmp`}T-{*n?-sX{3YlOoX1t;wKI@y|WW69*DhpiGt1sG?xET{{CnZoMg@amfX zJC-Ate5difZV7c@NfB!L6^NvKWwiZHVI^74o7;%1139 z4R$OE&JNgc`Tu{K@sV}F*3NWLi{&ePCjt+10ouJ}?NlAgpNU#9rkiPyoci;b+Kj|dzA8!4x!Z1~JOg}xi{vTR9|Iaig zn9&U!CsbIjPv?TE0h)9@+P>1yA!gS}cxrp_D+t-*s@Ll0IgvC>7Kn-0Qqa5mf@;I%jFk)^X^pry#`wexM%ku>-?71XY5*ikasyS?;~mI71J-mjCN zvmj~kpsmv(qP1S@as}!KYhLCKTjA*gPP)weIZDG+IjES z$zo&;qk%xHl(6$XOUj|gOJS|}lQV=teFag038#`hK{YY3i!iCj64XWyVPsrsGi_Ai pXz+|CiqWi~GFnm$oAU7oKhJTm1tGl!t-!V#gQu&X%Q~loCIDXX&oux5 literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..886440276800ad57d42d8f68558079a4e263acd8 GIT binary patch literal 4728 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~hKkh>GZx^prwfgH{PkH}&M z1~F9-X7mhF`2ZA@$#n4#0n$uB3<4*BSOZD}+1#8}t)`n87`U!_x;TbZFuu9rSfm^- za3tW~|4UO!8lt!*|MwnVeQ!bFW0u?V-#`ESHUD^H@sWR4b@u-_C4c;yZ1mxGynDLM zMho|M>ytBVmcA)p{l`;sL&V0TT@N#MH7wy-a!vTm{rkM}3aaeq9@Z!RytIS&#+zx3 zjGek=Jj<_K`=99f_uszuuT$06{&Z1R*m$(>;h*yv)9t!gRZs z`S5+l@4n}pRhI&5b$4JvSMYoKPi_CG&z)`#N!_xC1qB#AB@J%sGBXK9zme!`Sn}-i z)b~BVZKLrh_?PD8(XxRDO9?of%Z3k`4;LghFtSNR7%C`ePd{{$ftlwBuzGl-P#}@e&?w5xw5dpQ z4v)bBhcmzen~$k|ROx64jE2BS4uJ#wz;^Ec|8F%@gcumuML-SQw^ujvGB9u)HuzV+ z>(7LnZ(?v2qV&M?rU=lY|1JVi(2~3YTL9_xVhewjJl0yRnBjZPAkkkYg7L|>X zf(i}|4GllQx;&VeCf(=(sdjMq!vGReVPy2oaRaGVP^bfHWn!7s(4b1FYK{(hplJ+^DhdjnXFEWuS&r}nO>1xv5}0r#%>|^I=@I*=b4EjFH0_M$ ktkH63v{a^ICGmjq`ZlixpM5ia11m}fPgg&ebxsLQ0QI-*zW@LL literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4e46d0f9dd951aae44acb3a06104cc1f9c548157 GIT binary patch literal 4497 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~hKkh>GZx^prwfgH{PkH}&M z1~F9-X7mhF`2ZA@$#n4#0n$uB3<4*BSOZD}*&G%OWoh0(n#I$_F{Fa=&DDdP%!VS) zj^FQZxqiFJPsm5X{cw7~r*mHw9U2%InHmpp-m7o|u~^nMNH78^4i_dSp^cR;ATg#S zW)=k^m5ouZE4ac1C)1O8sD=1jo%T5?hI_`_LGp>7fj#~WbgYk0Hf>QWYt5KbU7 zg!hu+sOV@ojHZXte1TaKc!WCG0h<>@!| zpEzE5u;F9imX5I1A3pyA*6RLdCfkqV|%R@@W6w-2DKQ3 zfGic>J|$2iLV)p+D9}0uP9SR%n>(mw!P3F-kqcj1 zTr$-H=Ai?74_y0n3wrfs4)<+*BM7FN+q*Cc3DukJ%w##;9rcc*e zs}Jo|tdZKsO~%-grU$ulE6P`muWlHTk1@mC_hhT*{&)Yme_iuD=lPv^fA9C#?>)co z12*~SGZryuG@8DjuU8NWh)jq?plpwZ0isR!{3 zv>OJX)m|@7Fsvv%)K3P_(%)BjXY>X+s20Im=I6C8I0+ee(iCGE%Jf>N(QwwD@u_nr z44&*%bTEWEmd}mKTOKOMUa&Db6DJ}T4@bYv@}XYI>R4nYiUN46E?st_!IMJ3aaV$* zA*cg_gxyHagf|SUnfIR39>*PA4UK@1BwMHrdNGFWyx+T+V!>ou!Uiq<97z_peC0*4 zKzC?S97~OvLesnqiUmRJr#7$<6fN5^%A{D(GxguA;!hAE`>ky$*~krQV@|36##8B8 z)#BXq{FM35on=KCdUV-KtxQQ((z_bDMsb&oq3C&2;!r$>71mWR)x|K(j{H|Qo$i~H zzS)=#EA7ccBc^bL)@3^gppbU`%x5_Mr+MG_K^ovlFw2J^D&}XMhPV;v)lJzXdC+2^ zeL9363&^)Zkk`&%_@fAd=wA5a8WerJE8;8&M2+VJ&bf3hY39n}|F6@OXcG73dK=52 zv(}DH7R`v-O`GI8y0|Tl8-@62dlBl;I6i{h)+DM=xh-HKO4+dbaXEu7+jhHAxeK+$ z`--RA+)RZDQdNJRUMA_K)0=dhB!g4+Csa(>p4j%Q+thL#=X$G^J{aaj@OJuU2kX&& z$DSp$8?taTzI|+}TL26%2l9rNA&8MsG*6!4v<$~{)X%5bw@A9L#y{>C%1u$A|BL%nS`HuI7!N%!oUx;sz2}_jJ*y zWv3&KE6mv46S|qCmGjtTZV-_@GnL@vgrldo<Iwe5c#-e-v*%6a#> zk`V7#2A_e06obW9KQ8LwVjvoU?jC?

wT8Ccwub3i1p1#;2XDLD%8QHnWV zb%`YDvcH;)Olku?%6)9gz61c%#5XQ%7LGS2D_`qqLnN$!JYjRElq9!L);GPofm@$Rx`DqfHZ~`k# z%y)0)z$nP^s~F9oFl>$uXKaIPa+E%8l%54ZUueHcp*CjcnA)EPQawfde@5%cHghLb z;UN&owCK@1cNh~3PD_;vQ;?~wk#xX_XYonrE;bCY8qKv}eI`t=dDQee*s?jsD%4ne z^B}*uOdaKEOdQP`dXevQ4Ebu&ny^FR6=)-yH|32pC*R8ZIGXmA^z0U2l+B-x95PQ= zm@@_WN$M0cp3zdQIOO8KpIo;k=BFNw$!^ZD9EyptqrKyL&s(5LA2c+Z4grqwn%9D4ljC)AIEwOey`C7{P zRgr@F7I56)XmYE_1Tb2A#=2|(U>Kx4R$v4)%KECwn_?(CadONk2Qt!GT4+67hoU39 zJhW!)s45UihEo*aE5)IoTw*^1jGI~3b|y?=*7$e?DDgsrwI!t>w+}or~1j)8L`<2An3nC$Lef~o22l-I=YNG0M zN?wn>b3`~n^a}b{*#rQ_Dz|4rd?0bZ+|$V0N&w_u-7CYYVv?*JX!fV6>l#sT2Z6?UExW0tv#u{oYMb!a%Ia4K)|K_)NE$L-dme zYL$B%fj;W0&15d5w9Zqt+JPZcn(m>27k_AQJ_H(`-y}czlQ)Pz`Lwl2?F}lU@^a}C zO66bmM-rt+LEPPCk}emjj=P>8>E=K}vVK#)73e~DN!=f$z7`6u4|SKkj522;7U3Mh z_(pK_Mq#a*OPQsye94zw1}^ez29;+(>FPyCz5|1IbQQ@#>5`$DD-&kI^`hzOL`vzd z3gu=Fe6p~9ld1$1VB!xGJn3O8G~ENZ6}C9Il*3ogo3A>0_ks}}yK^NnE>xBH%Nc(K?B1Id2tB%Lrc1Asz*;;@E6&-~gK zlMTy*8xE=#nv|Z#7znZ~xWP)5T5)`f@d z{zjw7GV=rTpUEuUk5aSK+}nwd%(ZuJsQw@|VPN$2yd58yFT*3zk5VHGIBv`j%r_tw z|7lYCN9J=T%|&T0@!)ivtEEpJ?^Am)#)&StjTC@F4mx;&0HHd*#S*RUB z{@&)`mTEPOr8)+!4nQ>9)HcA~l@+u%t&9OvAewEQOW;P!fKHdCdyby~qS@Kx3|#Qc zP9`bdKs4Je{Xn7?H0ds(#1@EV^#h63aL=+PP8NO&h-Mq}oSOejZ+6+tf8c*X%cjF0 Q=2e=X_a?6rkBFoH2A45}E&u=k literal 0 HcmV?d00001 diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8277b97c003accf06b6cb5768b24c83f1d4f11a5 GIT binary patch literal 4289 zcmeAS@N?(olHy`uVBq!ia0y~yV4MKL9Be?5hW%z|fD~hKkh>GZx^prwfgH{PkH}&M z1~F9-X7mhF`2ZA@$#n4#0n$uB3<4*BSOZD}+3fG5fV8KB1B;4opCS_r2S-Q44kjS2!pJB%VT(l@hk$~D0@EWdAU%PF z#l_*$BMG3X4GkO}+6oR04nhI~jGo=e9ixVghS_M+ph7hhsTqEG$93t1YZY-YkEoiz>-9X ziAl(E%1oeigs{Mbt=%`k