diff --git a/AsyncDisplayKit/ASButtonNode.mm b/AsyncDisplayKit/ASButtonNode.mm index 5e40ace0..36282134 100644 --- a/AsyncDisplayKit/ASButtonNode.mm +++ b/AsyncDisplayKit/ASButtonNode.mm @@ -78,7 +78,7 @@ // of the button node to add a touch handler. [_titleNode setLayerBacked:YES]; #endif - _titleNode.style.flexShrink = YES; + _titleNode.style.flexShrink = 1; } return _titleNode; } diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/AsyncDisplayKit/ASVideoPlayerNode.mm index 049b2fbb..cd1068ec 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.mm +++ b/AsyncDisplayKit/ASVideoPlayerNode.mm @@ -406,7 +406,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; return slider; }]; - _scrubberNode.style.flexShrink = YES; + _scrubberNode.style.flexShrink = 1; [_cachedControls setObject:_scrubberNode forKey:@(ASVideoPlayerNodeControlTypeScrubber)]; } @@ -418,7 +418,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; { if (_controlFlexGrowSpacerSpec == nil) { _controlFlexGrowSpacerSpec = [[ASStackLayoutSpec alloc] init]; - _controlFlexGrowSpacerSpec.style.flexGrow = YES; + _controlFlexGrowSpacerSpec.style.flexGrow = 1; } [_cachedControls setObject:_controlFlexGrowSpacerSpec forKey:@(ASVideoPlayerNodeControlTypeFlexGrowSpacer)]; @@ -735,7 +735,7 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext; _scrubberNode.style.height = ASDimensionMakeWithPoints(44.0); ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; - spacer.style.flexGrow = YES; + spacer.style.flexGrow = 1; ASStackLayoutSpec *controlbarSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:10.0 diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index 20439eec..aa01b818 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -37,8 +37,8 @@ typedef struct ASEnvironmentStateExtensions { typedef struct ASEnvironmentLayoutOptionsState { CGFloat spacingBefore;// = 0; CGFloat spacingAfter;// = 0; - BOOL flexGrow;// = NO; - BOOL flexShrink;// = NO; Default value is YES if created via ASEnvironmentLayoutOptionsStateMakeDefault + CGFloat flexGrow;// = 0; + CGFloat flexShrink;// = 0; ASDimension flexBasis;// = ASRelativeDimensionAuto; ASStackLayoutAlignSelf alignSelf;// = ASStackLayoutAlignSelfAuto; CGFloat ascender;// = 0; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 903b159f..979ff381 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -269,16 +269,18 @@ extern NSString * const ASLayoutableStyleLayoutPositionProperty; @property (nonatomic, assign) CGFloat spacingAfter; /** - * @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? - * Used when attached to a stack layout. + * @abstract If the sum of childrens' stack dimensions is less than the minimum size, how much should this component grow? + * This value represents the "flex grow factor" and determines how much this component should grow in relation to any + * other flexible children. */ -@property (nonatomic, assign) BOOL flexGrow; +@property (nonatomic, assign) CGFloat flexGrow; /** - * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? - * Used when attached to a stack layout. + * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, how much should this component shrink? + * This value represents the "flex shrink factor" and determines how much this component should shink in relation to + * other flexible children. */ -@property (nonatomic, assign) BOOL flexShrink; +@property (nonatomic, assign) CGFloat flexShrink; /** * @abstract Specifies the initial size in the stack dimension for this object. diff --git a/AsyncDisplayKit/Layout/ASLayoutable.mm b/AsyncDisplayKit/Layout/ASLayoutable.mm index 26631888..5ea59518 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.mm +++ b/AsyncDisplayKit/Layout/ASLayoutable.mm @@ -251,14 +251,14 @@ do {\ ASLayoutableStyleCallDelegate(ASLayoutableStyleSpacingAfterProperty); } -- (void)setFlexGrow:(BOOL)flexGrow +- (void)setFlexGrow:(CGFloat)flexGrow { ASDN::MutexLocker l(__instanceLock__); _flexGrow = flexGrow; ASLayoutableStyleCallDelegate(ASLayoutableStyleFlexGrowProperty); } -- (void)setFlexShrink:(BOOL)flexShrink +- (void)setFlexShrink:(CGFloat)flexShrink { ASDN::MutexLocker l(__instanceLock__); _flexShrink = flexShrink; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 9932d527..82e6652d 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -31,16 +31,18 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readwrite) CGFloat spacingAfter; /** - * @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? - * Used when attached to a stack layout. + * @abstract If the sum of childrens' stack dimensions is less than the minimum size, how much should this component grow? + * This value represents the "flex grow factor" and determines how much this component should grow in relation to any + * other flexible children. */ -@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) CGFloat flexGrow; /** - * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? - * Used when attached to a stack layout. + * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, how much should this component shrink? + * This value represents the "flex shrink factor" and determines how much this component should shink in relation to + * other flexible children. */ -@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) CGFloat flexShrink; /** * @abstract Specifies the initial size in the stack dimension for this object. diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 7abc7879..1d812ae5 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -15,6 +15,31 @@ #import "ASLayoutSpecUtilities.h" +static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style, + const idchild, + const CGFloat stackMax, + const CGFloat crossMax) +{ + // stretched children may have a cross direction max that is smaller than the minimum size constraint of the parent. + + const CGFloat computedMax = (style.direction == ASStackLayoutDirectionVertical ? + ASLayoutableSizeResolve(child.style.size, ASLayoutableParentSizeUndefined).max.width : + ASLayoutableSizeResolve(child.style.size, ASLayoutableParentSizeUndefined).max.height); + return computedMax == INFINITY ? crossMax : computedMax; +} + +static CGFloat resolveCrossDimensionMinForStretchChild(const ASStackLayoutSpecStyle &style, + const idchild, + const CGFloat stackMax, + const CGFloat crossMin) +{ + // stretched children will have a cross dimension of at least crossMin, unless they explicitly define a child size + // that is smaller than the constraint of the parent. + return (style.direction == ASStackLayoutDirectionVertical ? + ASLayoutableSizeResolve(child.style.size, ASLayoutableParentSizeUndefined).min.width : + ASLayoutableSizeResolve(child.style.size, ASLayoutableParentSizeUndefined).min.height) ?: crossMin; +} + /** Sizes the child given the parameters specified, and returns the computed layout. */ @@ -28,8 +53,11 @@ static ASLayout *crossChildLayout(const id child, { const ASStackLayoutAlignItems alignItems = alignment(child.style.alignSelf, style.alignItems); // stretched children will have a cross dimension of at least crossMin - const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; - const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); + const CGFloat childCrossMin = (alignItems == ASStackLayoutAlignItemsStretch ? resolveCrossDimensionMinForStretchChild(style, child, stackMax, crossMin) : 0); + const CGFloat childCrossMax = (alignItems == ASStackLayoutAlignItemsStretch ? + resolveCrossDimensionMaxForStretchChild(style, child, stackMax, crossMax) : + crossMax); + const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, childCrossMax); ASLayout *layout = [child layoutThatFits:childSizeRange parentSize:size]; ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child); return layout ? : [ASLayout layoutWithLayoutable:child size:{0, 0}]; @@ -93,6 +121,120 @@ static void stretchChildrenAlongCrossDimension(std::vector flexFactorInViolationDirection(const CGFloat violation) +{ + if (fabs(violation) < kViolationEpsilon) { + return [](const ASStackUnpositionedItem &item) { return 0; }; + } else if (violation > 0) { + return [](const ASStackUnpositionedItem &item) { return item.child.style.flexGrow; }; + } else { + return [](const ASStackUnpositionedItem &item) { return item.child.style.flexShrink; }; + } +} + +static inline CGFloat scaledFlexShrinkFactor(const ASStackUnpositionedItem &item, const ASStackLayoutSpecStyle &style) +{ + return stackDimension(style.direction, item.layout.size) * item.child.style.flexShrink; +} + +/** + Returns a lambda that computes a flex shrink adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param style The layout style to be applied to all children. + @param violation The amount that the stack layout violates its size range. + @return A lambda capable of computing the flex shrink adjustment, if any, for a particular item. + */ +static std::function flexShrinkAdjustment(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const CGFloat violation) +{ + const CGFloat scaledFlexShrinkFactorSum = std::accumulate(items.begin(), items.end(), 0, [&](CGFloat x, const ASStackUnpositionedItem &item) { + return x + scaledFlexShrinkFactor(item, style); + }); + return [style, scaledFlexShrinkFactorSum, violation](const ASStackUnpositionedItem &item, BOOL isFirstFlex) { + const CGFloat scaledFlexShrinkFactorRatio = scaledFlexShrinkFactor(item, style) / scaledFlexShrinkFactorSum; + // The item should shrink proportionally to the scaled flex shrink factor ratio computed above. + // Unlike the flex grow adjustment the flex shrink adjustment needs to take the size of each item into account. + return -fabs(scaledFlexShrinkFactorRatio * violation); + }; +} + +/** + Returns a lambda that computes a flex grow adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param violation The amount that the stack layout violates its size range. + @param flexFactorSum The sum of each item's flex factor as determined by the provided violation. + @return A lambda capable of computing the flex grow adjustment, if any, for a particular item. + */ +static std::function flexGrowAdjustment(const std::vector &items, + const CGFloat violation, + const CGFloat flexFactorSum) +{ + const CGFloat violationPerFlexFactor = floorf(violation / flexFactorSum); + const CGFloat remainingViolation = violation - (violationPerFlexFactor * flexFactorSum); + // To compute the flex grow adjustment distribute the violation proportionally based on each item's flex grow factor. + // If there happens to be a violation remaining make sure it is allocated to the first flexible child. + return [violationPerFlexFactor, remainingViolation](const ASStackUnpositionedItem &item, BOOL isFirstFlex) { + // Only apply the remaining violation for the first flexible child that has a flex grow factor. + return violationPerFlexFactor * item.child.style.flexGrow + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0); + }; +} + +/** + Returns a lambda that computes a flex adjustment for a given item based on the provided violation. + @param items The unpositioned items from the original unconstrained layout pass. + @param style The layout style to be applied to all children. + @param violation The amount that the stack layout violates its size range. + @param flexFactorSum The sum of each item's flex factor as determined by the provided violation. + @return A lambda capable of computing the flex adjustment for a particular item. + */ +static std::function flexAdjustmentInViolationDirection(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const CGFloat violation, + const CGFloat flexFactorSum) +{ + if (violation > 0) { + return flexGrowAdjustment(items, violation, flexFactorSum); + } else { + return flexShrinkAdjustment(items, style, violation); + } +} + +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +{ + return child.style.flexGrow > 0 && child.style.flexShrink > 0; +} + +/** + The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size + these children at zero size so that the children layouts are at least present. + */ +static void layoutFlexibleChildrenAtZeroSize(std::vector &items, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange, + const CGSize size) +{ + for (ASStackUnpositionedItem &item : items) { + const id child = item.child; + if (isFlexibleInBothDirections(child)) { + item.layout = crossChildLayout(child, + style, + 0, + 0, + crossDimension(style.direction, sizeRange.min), + crossDimension(style.direction, sizeRange.max), + size); + } + } +} + /** Computes the consumed stack dimension length for the given vector of children and stacking style. @@ -172,30 +314,6 @@ static CGFloat computeViolation(const CGFloat stackDimensionSum, return 0; } -/** The threshold that determines if a violation has actually occurred. */ -static const CGFloat kViolationEpsilon = 0.01; - -/** - Returns a lambda that determines if the given unpositioned item's child is flexible in the direction of the violation. - - @param violation the amount that the stack layout violates its size range. See header for sign interpretation. - */ -static std::function isFlexibleInViolationDirection(const CGFloat violation) -{ - if (std::fabs(violation) < kViolationEpsilon) { - return [](const ASStackUnpositionedItem &l) { return NO; }; - } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return l.child.style.flexGrow; }; - } else { - return [](const ASStackUnpositionedItem &l) { return l.child.style.flexShrink; }; - } -} - -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) -{ - return child.style.flexGrow && child.style.flexShrink; -} - /** If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific number then we may avoid the first "intrinsic" size calculation. @@ -210,29 +328,6 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector stackDimension(style.direction, sizeRange.max))); } -/** - The flexible children may have been left not laid out in the initial layout pass, so we may have to go through and size - these children at zero size so that the children layouts are at least present. - */ -static void layoutFlexibleChildrenAtZeroSize(std::vector &items, - const ASStackLayoutSpecStyle &style, - const ASSizeRange &sizeRange, - const CGSize size) -{ - for (ASStackUnpositionedItem &item : items) { - const id child = item.child; - if (isFlexibleInBothDirections(child)) { - item.layout = crossChildLayout(child, - style, - 0, - 0, - crossDimension(style.direction, sizeRange.min), - crossDimension(style.direction, sizeRange.max), - size); - } - } -} - /** Flexes children in the stack axis to resolve a min or max stack size violation. First, determines which children are flexible (see computeViolation and isFlexibleInViolationDirection). Then computes how much to flex each flexible child @@ -243,7 +338,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector &items, const ASStackLayoutSpecStyle &style, @@ -251,32 +347,32 @@ static void flexChildrenAlongStackDimension(std::vector const CGSize size, const BOOL useOptimizedFlexing) { - const CGFloat stackDimensionSum = computeStackDimensionSum(items, style); - const CGFloat violation = computeViolation(stackDimensionSum, style, sizeRange); - - // We count the number of children which are flexible in the direction of the violation - std::function isFlex = isFlexibleInViolationDirection(violation); - const NSUInteger flexibleChildren = std::count_if(items.begin(), items.end(), isFlex); - if (flexibleChildren == 0) { - // If optimized flexing was used then we have to clean up the unsized children, and lay them out at zero size + const CGFloat violation = computeViolation(computeStackDimensionSum(items, style), style, sizeRange); + std::function flexFactor = flexFactorInViolationDirection(violation); + // The flex factor sum is needed to determine if flexing is necessary. + // This value is also needed if the violation is positive and flexible children need to grow, so keep it around. + const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0, [&](CGFloat x, const ASStackUnpositionedItem &item) { + return x + flexFactor(item); + }); + // If no children are able to flex then there is nothing left to do. Bail. + if (flexFactorSum == 0) { + // If optimized flexing was used then we have to clean up the unsized children and lay them out at zero size. if (useOptimizedFlexing) { layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, size); } return; } - - // Each flexible child along the direction of the violation is expanded or contracted equally - const CGFloat violationPerFlexChild = std::floor(violation / flexibleChildren); - // If the floor operation above left a remainder we may have a remainder after deducting the adjustments from all the - // contributions of the flexible children. - const CGFloat violationRemainder = violation - (violationPerFlexChild * flexibleChildren); - + std::function flexAdjustment = flexAdjustmentInViolationDirection(items, + style, + violation, + flexFactorSum); BOOL isFirstFlex = YES; for (ASStackUnpositionedItem &item : items) { - if (isFlex(item)) { + const CGFloat currentFlexAdjustment = flexAdjustment(item, isFirstFlex); + // Children are consider inflexible if they do not need to make a flex adjustment. + if (currentFlexAdjustment != 0) { const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size); - // The first flexible child is given the additional violation remainder - const CGFloat flexedStackSize = originalStackSize + violationPerFlexChild + (isFirstFlex ? violationRemainder : 0); + const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment; item.layout = crossChildLayout(item.child, style, MAX(flexedStackSize, 0), diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index e9e59b41..49475e96 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -90,7 +90,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce { ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10)); - foregroundNode.style.flexGrow = YES; + foregroundNode.style.flexGrow = 1; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm index 4ceeeb98..3fa2df4d 100644 --- a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm @@ -107,7 +107,7 @@ static NSString *suffixForPositionOptions(ASRelativeLayoutSpecPosition horizonta { ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10)); - foregroundNode.style.flexGrow = YES; + foregroundNode.style.flexGrow = 1; ASLayoutSpec *childSpec = [ASBackgroundLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index ecc6263a..49354e75 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -25,10 +25,10 @@ static NSArray *defaultSubnodes() { - return defaultSubnodesWithSameSize(CGSizeZero, NO); + return defaultSubnodesWithSameSize(CGSizeZero, 0); } -static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) +static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, CGFloat flex) { NSArray *subnodes = @[ ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize), @@ -62,7 +62,7 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) } - (void)testStackLayoutSpecWithJustify:(ASStackLayoutJustifyContent)justify - flex:(BOOL)flex + flexFactor:(CGFloat)flex sizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier { @@ -81,7 +81,7 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment identifier:(NSString *)identifier { - NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; stackLayoutSpec.direction = direction; @@ -139,34 +139,34 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) { // width 300px; height 0-300px static ASSizeRange kSize = {{300, 0}, {300, 300}}; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flex:NO sizeRange:kSize identifier:@"justifyStart"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentCenter flex:NO sizeRange:kSize identifier:@"justifyCenter"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentEnd flex:NO sizeRange:kSize identifier:@"justifyEnd"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flex:YES sizeRange:kSize identifier:@"flex"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flex:NO sizeRange:kSize identifier:@"justifySpaceBetween"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flex:NO sizeRange:kSize identifier:@"justifySpaceAround"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flexFactor:0 sizeRange:kSize identifier:@"justifyStart"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentCenter flexFactor:0 sizeRange:kSize identifier:@"justifyCenter"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentEnd flexFactor:0 sizeRange:kSize identifier:@"justifyEnd"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flexFactor:1 sizeRange:kSize identifier:@"flex"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flexFactor:0 sizeRange:kSize identifier:@"justifySpaceBetween"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flexFactor:0 sizeRange:kSize identifier:@"justifySpaceAround"]; } - (void)testOverflowBehaviors { // width 110px; height 0-300px static ASSizeRange kSize = {{110, 0}, {110, 300}}; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flex:NO sizeRange:kSize identifier:@"justifyStart"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentCenter flex:NO sizeRange:kSize identifier:@"justifyCenter"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentEnd flex:NO sizeRange:kSize identifier:@"justifyEnd"]; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flex:YES sizeRange:kSize identifier:@"flex"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flexFactor:0 sizeRange:kSize identifier:@"justifyStart"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentCenter flexFactor:0 sizeRange:kSize identifier:@"justifyCenter"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentEnd flexFactor:0 sizeRange:kSize identifier:@"justifyEnd"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentStart flexFactor:1 sizeRange:kSize identifier:@"flex"]; // On overflow, "space between" is identical to "content start" - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flex:NO sizeRange:kSize identifier:@"justifyStart"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flexFactor:0 sizeRange:kSize identifier:@"justifyStart"]; // On overflow, "space around" is identical to "content center" - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flex:NO sizeRange:kSize identifier:@"justifyCenter"]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flexFactor:0 sizeRange:kSize identifier:@"justifyCenter"]; } - (void)testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - subnodes[1].style.flexShrink = YES; + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); + subnodes[1].style.flexShrink = 1; // Width is 75px--that's less than the sum of the widths of the children, which is 100px. static ASSizeRange kSize = {{75, 0}, {75, 150}}; @@ -177,7 +177,7 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, YES); + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 1); setCGSizeToNode({150, 150}, subnodes[1]); // width 300px; height 0-150px. @@ -333,27 +333,27 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) { // width 301px; height 0-300px; 1px remaining static ASSizeRange kSize = {{301, 0}, {301, 300}}; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flex:NO sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceBetween flexFactor:0 sizeRange:kSize identifier:nil]; } - (void)testJustifiedSpaceAroundWithRemainingSpace { // width 305px; height 0-300px; 5px remaining static ASSizeRange kSize = {{305, 0}, {305, 300}}; - [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flex:NO sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithJustify:ASStackLayoutJustifyContentSpaceAround flexFactor:0 sizeRange:kSize identifier:nil]; } - (void)testChildThatChangesCrossSizeWhenMainSizeIsFlexed { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - ASDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); - ASDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); + ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); + ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; child1.style.flexBasis = ASDimensionMakeWithFraction(1); - child1.style.flexGrow = YES; - child1.style.flexShrink = YES; + child1.style.flexGrow = 1; + child1.style.flexShrink = 1; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -366,13 +366,13 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) .alignItems = ASStackLayoutAlignItemsCenter }; - ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100}); - subnode1.style.flexShrink = YES; + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50}) + ]; + subnodes[0].style.flexShrink = 1; + subnodes[1].style.flexShrink = 1; - ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50}); - subnode2.style.flexShrink = YES; - - NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; [self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil]; } @@ -508,11 +508,11 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); setCGSizeToNode({150, 150}, subnodes[1]); for (ASDisplayNode *subnode in subnodes) { - subnode.style.flexGrow = YES; + subnode.style.flexGrow = 1; subnode.style.flexBasis = ASDimensionMakeWithPoints(10); } @@ -529,9 +529,9 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); for (ASDisplayNode *subnode in subnodes) { - subnode.style.flexGrow = YES; + subnode.style.flexGrow = 1; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. @@ -561,15 +561,20 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) - (void)testCrossAxisStretchingOccursAfterStackAxisFlexing { + // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. + // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. + NSArray *subnodes = @[ - ASDisplayNodeWithBackgroundColor([UIColor greenColor]), - ASDisplayNodeWithBackgroundColor([UIColor blueColor], {10, 0}), - ASDisplayNodeWithBackgroundColor([UIColor redColor], {3000, 3000}) + ASDisplayNodeWithBackgroundColor([UIColor greenColor]), // Inset background node + ASDisplayNodeWithBackgroundColor([UIColor blueColor]), // child1 of stack + ASDisplayNodeWithBackgroundColor([UIColor redColor], {500, 500}) // child2 of stack ]; - ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - child2.style.flexGrow = YES; - child2.style.flexShrink = YES; + subnodes[1].style.width = ASDimensionMake(10); + + ASDisplayNode *child2 = subnodes[2]; + child2.style.flexGrow = 1; + child2.style.flexShrink = 1; // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. @@ -592,29 +597,217 @@ static void setCGSizeToNode(CGSize size, ASDisplayNode *node) [self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:subnodes identifier:nil]; } -- (void)testViolationIsDistributedEquallyAmongFlexibleChildren +- (void)testPositiveViolationIsDistributedEquallyAmongFlexibleChildren { ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; - NSArray *subnodes = defaultSubnodes(); + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); + subnodes[0].style.flexGrow = 0; + subnodes[2].style.flexGrow = 0; - setCGSizeToNode({300, 50}, subnodes[0]); - setCGSizeToNode({100, 50}, subnodes[1]); - setCGSizeToNode({200, 50}, subnodes[2]); - - subnodes[0].style.flexShrink = YES; - subnodes[1].style.flexShrink = NO; - subnodes[2].style.flexShrink = YES; - - // A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, - // causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px. - // In the W3 flexbox standard, flexible children are shrunk proportionate to their original sizes, - // resulting in widths of 180px, 100px, and 120px. - // This test verifies the current behavior--the snapshot contains widths 300px, 100px, and 50px. - static ASSizeRange kSize = {{400, 0}, {400, 150}}; + // In this scenario a width of 350 results in a positive violation of 200. + // Due to each flexible subnode specifying a flex grow factor of 1 the violation will be distributed evenly. + static ASSizeRange kSize = {{350, 350}, {350, 350}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; } +- (void)testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; + + NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, 0); + subnodes[0].style.flexGrow = 1; + subnodes[1].style.flexGrow = 2; + subnodes[2].style.flexGrow = 1; + + // In this scenario a width of 350 results in a positive violation of 200. + // The first and third subnodes specify a flex grow factor of 1 and will flex by 50. + // The second subnode specifies a flex grow factor of 2 and will flex by 100. + static ASSizeRange kSize = {{350, 350}, {350, 350}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; + + const CGSize kSubnodeSize = {50, 50}; + NSArray *subnodes = defaultSubnodesWithSameSize(kSubnodeSize, 0); + subnodes = [subnodes arrayByAddingObject:ASDisplayNodeWithBackgroundColor([UIColor yellowColor], kSubnodeSize)]; + + subnodes[0].style.flexShrink = 1; + subnodes[1].style.flexGrow = 1; + subnodes[2].style.flexShrink = 0; + subnodes[3].style.flexGrow = 1; + + // In this scenario a width of 400 results in a positive violation of 200. + // The first and third subnode specify a flex shrink factor of 1 and 0, respectively. They won't flex. + // The second and fourth subnode specify a flex grow factor of 1 and will flex by 100. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; + + const CGSize kSubnodeSize = {50, 50}; + NSArray *subnodes = defaultSubnodesWithSameSize(kSubnodeSize, 0); + subnodes = [subnodes arrayByAddingObject:ASDisplayNodeWithBackgroundColor([UIColor yellowColor], kSubnodeSize)]; + + subnodes[0].style.flexShrink = 1; + subnodes[1].style.flexGrow = 3; + subnodes[2].style.flexShrink = 0; + subnodes[3].style.flexGrow = 1; + + // In this scenario a width of 400 results in a positive violation of 200. + // The first and third subnodes specify a flex shrink factor of 1 and 0, respectively. They won't flex. + // The second child subnode specifies a flex grow factor of 3 and will flex by 150. + // The fourth child subnode specifies a flex grow factor of 1 and will flex by 50. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testRemainingViolationIsAppliedProperlyToFirstFlexibleChild +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; + + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor greenColor], {50, 25}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 0}), + ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 100}) + ]; + + subnodes[0].style.flexGrow = 0; + subnodes[1].style.flexGrow = 1; + subnodes[2].style.flexGrow = 1; + + // In this scenario a width of 300 results in a positive violation of 175. + // The second and third subnodes specify a flex grow factor of 1 and will flex by 88 and 87, respectively. + static ASSizeRange kSize = {{300, 300}, {300, 300}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; + + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor greenColor], {300, 50}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {100, 50}), + ASDisplayNodeWithBackgroundColor([UIColor redColor], {200, 50}) + ]; + + subnodes[0].style.flexShrink = 1; + subnodes[1].style.flexShrink = 0; + subnodes[2].style.flexShrink = 1; + + // In this scenario a width of 400 results in a negative violation of 200. + // The first and third subnodes specify a flex shrink factor of 1 and will flex by -120 and -80, respectively. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; + + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor greenColor], {50, 300}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 100}), + ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 200}) + ]; + + subnodes[0].style.flexShrink = 2; + subnodes[1].style.flexShrink = 1; + subnodes[2].style.flexShrink = 2; + + // In this scenario a width of 400 results in a negative violation of 200. + // The first and third subnodes specify a flex shrink factor of 2 and will flex by -109 and -72, respectively. + // The second subnode specifies a flex shrink factor of 1 and will flex by -18. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; + + const CGSize kSubnodeSize = {150, 50}; + NSArray *subnodes = defaultSubnodesWithSameSize(kSubnodeSize, 0); + subnodes = [subnodes arrayByAddingObject:ASDisplayNodeWithBackgroundColor([UIColor yellowColor], kSubnodeSize)]; + + subnodes[0].style.flexGrow = 1; + subnodes[1].style.flexShrink = 1; + subnodes[2].style.flexGrow = 0; + subnodes[3].style.flexShrink = 1; + + // In this scenario a width of 400 results in a negative violation of 200. + // The first and third subnodes specify a flex grow factor of 1 and 0, respectively. They won't flex. + // The second and fourth subnodes specify a flex grow factor of 1 and will flex by -100. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; + + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor greenColor], {50, 150}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 100}), + ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 150}), + ASDisplayNodeWithBackgroundColor([UIColor yellowColor], {50, 200}) + ]; + + subnodes[0].style.flexGrow = 1; + subnodes[1].style.flexShrink = 1; + subnodes[2].style.flexGrow = 0; + subnodes[3].style.flexShrink = 3; + + // In this scenario a width of 400 results in a negative violation of 200. + // The first and third subnodes specify a flex grow factor of 1 and 0, respectively. They won't flex. + // The second subnode specifies a flex grow factor of 1 and will flex by -28. + // The fourth subnode specifies a flex grow factor of 3 and will flex by -171. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth +{ + ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; + + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor greenColor], {300, 50}), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], {100, 50}), + ASDisplayNodeWithBackgroundColor([UIColor redColor], {200, 50}) + ]; + + subnodes[0].style.flexShrink = 1; + subnodes[1].style.flexShrink = 2; + subnodes[2].style.flexShrink = 1; + + // In this scenario a width of 400 results in a negative violation of 200. + // The first and third subnodes specify a flex shrink factor of 1 and will flex by 50. + // The second subnode specifies a flex shrink factor of 2 and will flex by -57. It will have a width of 43. + static ASSizeRange kSize = {{400, 400}, {400, 400}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + +- (void)testNestedStackLayoutStretchDoesNotViolateWidth +{ + ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; // Default direction is horizontal + stackLayoutSpec.direction = ASStackLayoutDirectionHorizontal; + stackLayoutSpec.alignItems = ASStackLayoutAlignItemsStretch; + [stackLayoutSpec.style setSizeWithCGSize:{100, 100}]; + + ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); + stackLayoutSpec.children = @[child]; + + static ASSizeRange kSize = {{0, 0}, {300, INFINITY}}; + [self testStackLayoutSpec:stackLayoutSpec sizeRange:kSize subnodes:@[child] identifier:nil]; +} + - (void)testHorizontalAndVerticalAlignments { [self testStackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal itemsHorizontalAlignment:ASHorizontalAlignmentLeft itemsVerticalAlignment:ASVerticalAlignmentTop identifier:@"horizontalTopLeft"]; diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png index 8c266c57..ea22b081 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png index 013057b3..4be05212 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png index a349c400..537b1f6f 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png index 35954a4d..8c021540 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png index 1a31b256..8fbd29f3 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png index 51d8ffcf..092dc950 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png index 3a3b3740..cd7489cb 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png index 3e668e16..64727283 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png index 91920664..eebc4bb9 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png index a590ed92..1b82cbb9 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@2x.png new file mode 100644 index 00000000..ebea9c1e Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@3x.png new file mode 100644 index 00000000..1cfcea8a Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@2x.png new file mode 100644 index 00000000..53185596 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@3x.png new file mode 100644 index 00000000..0ba9ef52 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAmongGrowingAndShrinkingFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@2x.png new file mode 100644 index 00000000..4dda03f9 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@3x.png new file mode 100644 index 00000000..43440d20 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@2x.png new file mode 100644 index 00000000..8792b39b Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@3x.png new file mode 100644 index 00000000..df6aaf64 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorAmongGrowingAndShrinkingFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@2x.png new file mode 100644 index 00000000..405fa880 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@3x.png new file mode 100644 index 00000000..bd022030 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedProportionallyBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWidth@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png new file mode 100644 index 00000000..2a5fd841 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png new file mode 100644 index 00000000..571449ba Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png index 6c7985ff..3c15de51 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png index c3132117..c602d388 100644 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@2x.png new file mode 100644 index 00000000..30425f63 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@3x.png new file mode 100644 index 00000000..8e0939e9 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@2x.png new file mode 100644 index 00000000..5ab4b9fd Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@3x.png new file mode 100644 index 00000000..a8d65774 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongGrowingAndShrinkingFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@2x.png new file mode 100644 index 00000000..262cf4c2 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@3x.png new file mode 100644 index 00000000..573dea9c Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@2x.png new file mode 100644 index 00000000..584294eb Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@3x.png new file mode 100644 index 00000000..cb8efb68 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongGrowingAndShrinkingFlexibleChildren@3x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png new file mode 100644 index 00000000..c5551579 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png new file mode 100644 index 00000000..ee836d5c Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png differ