diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj
index fcf1c500b0..bea602effe 100644
--- a/AsyncDisplayKit.xcodeproj/project.pbxproj
+++ b/AsyncDisplayKit.xcodeproj/project.pbxproj
@@ -1996,6 +1996,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
@@ -2106,7 +2107,6 @@
"DEBUG=1",
"$(inherited)",
"COCOAPODS=1",
- "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
@@ -2132,7 +2132,6 @@
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"COCOAPODS=1",
- "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
@@ -2193,6 +2192,7 @@
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
+ GCC_PREPROCESSOR_DEFINITIONS = "";
INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -2260,7 +2260,6 @@
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"COCOAPODS=1",
- "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
@@ -2281,6 +2280,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
@@ -2307,6 +2307,7 @@
GCC_NO_COMMON_BLOCKS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch";
+ GCC_PREPROCESSOR_DEFINITIONS = "";
INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
diff --git a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme
index 5421b65a77..f37a5b1a83 100644
--- a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme
+++ b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme
@@ -75,6 +75,13 @@
ReferencedContainer = "container:AsyncDisplayKit.xcodeproj">
+
+
+
+
diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h
index 772c1022e4..79281d73dc 100644
--- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h
+++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h
@@ -86,6 +86,22 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
ASStackLayoutAlignSelfStretch,
};
+// TODO documentation
+typedef NS_ENUM(NSUInteger, ASStackLayoutFlexWrap) {
+ ASStackLayoutFlexWrapNoWrap,
+ ASStackLayoutFlexWrapWrap,
+};
+
+// TODO documentation
+typedef NS_ENUM(NSUInteger, ASStackLayoutAlignContent) {
+ ASStackLayoutAlignContentStart,
+ ASStackLayoutAlignContentCenter,
+ ASStackLayoutAlignContentEnd,
+ ASStackLayoutAlignContentSpaceBetween,
+ ASStackLayoutAlignContentSpaceAround,
+ ASStackLayoutAlignContentStretch,
+};
+
/** Orientation of children along horizontal axis */
typedef NS_ENUM(NSUInteger, ASHorizontalAlignment) {
/** No alignment specified. Default value */
diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h
index d805f1e9dc..d218db7b42 100644
--- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h
+++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h
@@ -59,6 +59,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent;
/** Orientation of children along cross axis. Defaults to ASStackLayoutAlignItemsStretch */
@property (nonatomic, assign) ASStackLayoutAlignItems alignItems;
+//TODO documentation. Defaults to ASStackLayoutFlexWrapNoWrap
+@property (nonatomic, assign) ASStackLayoutFlexWrap flexWrap;
+//TODO documentation. Defaults to ASStackLayoutAlignContentStart
+@property (nonatomic, assign) ASStackLayoutAlignContent alignContent;
- (instancetype)init;
@@ -69,7 +73,27 @@ NS_ASSUME_NONNULL_BEGIN
@param alignItems Orientation of the children along the cross axis
@param children ASLayoutElement children to be positioned.
*/
-+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray> *)children AS_WARN_UNUSED_RESULT;
++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction
+ spacing:(CGFloat)spacing
+ justifyContent:(ASStackLayoutJustifyContent)justifyContent
+ alignItems:(ASStackLayoutAlignItems)alignItems
+ children:(NSArray> *)children AS_WARN_UNUSED_RESULT;
+
+/**
+ @param direction The direction of the stack view (horizontal or vertical)
+ @param spacing The spacing between the children
+ @param justifyContent If no children are flexible, this describes how to fill any extra space
+ @param alignItems Orientation of the children along the cross axis
+ @param children ASLayoutElement children to be positioned.
+ TODO documentation flex wrap and align content
+ */
++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction
+ spacing:(CGFloat)spacing
+ justifyContent:(ASStackLayoutJustifyContent)justifyContent
+ alignItems:(ASStackLayoutAlignItems)alignItems
+ flexWrap:(ASStackLayoutFlexWrap)flexWrap
+ alignContent:(ASStackLayoutAlignContent)alignContent
+ children:(NSArray> *)children AS_WARN_UNUSED_RESULT;
/**
* @return A stack layout spec with direction of ASStackLayoutDirectionVertical
diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm
index 1089592f91..1a78a5428c 100644
--- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm
+++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm
@@ -25,12 +25,17 @@
- (instancetype)init
{
- return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:nil];
+ return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart children:nil];
}
+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children
{
- return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems children:children];
+ return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart children:children];
+}
+
++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent children:(NSArray> *)children
+{
+ return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:flexWrap alignContent:alignContent children:children];
}
+ (instancetype)verticalStackLayoutSpec
@@ -47,7 +52,7 @@
return stackLayoutSpec;
}
-- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children
+- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent children:(NSArray *)children
{
if (!(self = [super init])) {
return nil;
@@ -58,6 +63,8 @@
_verticalAlignment = ASVerticalAlignmentNone;
_alignItems = alignItems;
_justifyContent = justifyContent;
+ _flexWrap = flexWrap;
+ _alignContent = alignContent;
[self setChildren:children];
return self;
@@ -127,7 +134,7 @@
return {child, style, style.size};
});
- const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems};
+ const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .flexWrap = _flexWrap, .alignContent = _alignContent};
const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize);
const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize);
@@ -137,14 +144,12 @@
self.style.descender = stackChildren.back().style.descender;
}
- const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, unpositionedLayout.crossSize);
-
NSMutableArray *sublayouts = [NSMutableArray array];
- for (const auto &l : positionedLayout.items) {
- [sublayouts addObject:l.layout];
+ for (const auto &item : positionedLayout.items) {
+ [sublayouts addObject:item.layout];
}
-
- return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts];
+
+ return [ASLayout layoutWithLayoutElement:self size:positionedLayout.size sublayouts:sublayouts];
}
- (void)resolveHorizontalAlignment
diff --git a/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h
index 6bee0a6866..fbcafdff41 100644
--- a/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h
+++ b/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h
@@ -15,6 +15,8 @@ typedef struct {
CGFloat spacing;
ASStackLayoutJustifyContent justifyContent;
ASStackLayoutAlignItems alignItems;
+ ASStackLayoutFlexWrap flexWrap;
+ ASStackLayoutAlignContent alignContent;
} ASStackLayoutSpecStyle;
inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size)
@@ -42,6 +44,10 @@ inline CGSize directionSize(const ASStackLayoutDirection direction, const CGFloa
return (direction == ASStackLayoutDirectionVertical) ? CGSizeMake(cross, stack) : CGSizeMake(stack, cross);
}
+inline void setStackValueToPoint(const ASStackLayoutDirection direction, const CGFloat stack, CGPoint &point) {
+ (direction == ASStackLayoutDirectionVertical) ? (point.y = stack) : (point.x = stack);
+}
+
inline ASSizeRange directionSizeRange(const ASStackLayoutDirection direction,
const CGFloat stackMin,
const CGFloat stackMax,
diff --git a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h
index d4c7b3628a..dd0b697555 100644
--- a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h
+++ b/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h
@@ -15,7 +15,9 @@
/** Represents a set of laid out and positioned stack layout children. */
struct ASStackPositionedLayout {
const std::vector items;
-
+ /** Final size of the stack */
+ const CGSize size;
+
/** Given an unpositioned layout, computes the positions each child should be placed at. */
static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout,
const ASStackLayoutSpecStyle &style,
diff --git a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm
index 870bfec09b..c39bdcdab6 100644
--- a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm
+++ b/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm
@@ -11,24 +11,26 @@
#import
#import
+#import
+#import
#import
#import
#import
-static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
- const ASStackLayoutSpecItem &l,
- const CGFloat crossSize,
- const CGFloat baseline)
+static CGFloat crossOffsetForItem(const ASStackLayoutSpecItem &item,
+ const ASStackLayoutSpecStyle &style,
+ const CGFloat crossSize,
+ const CGFloat baseline)
{
- switch (alignment(l.child.style.alignSelf, style.alignItems)) {
+ switch (alignment(item.child.style.alignSelf, style.alignItems)) {
case ASStackLayoutAlignItemsEnd:
- return crossSize - crossDimension(style.direction, l.layout.size);
+ return crossSize - crossDimension(style.direction, item.layout.size);
case ASStackLayoutAlignItemsCenter:
- return ASFloorPixelValue((crossSize - crossDimension(style.direction, l.layout.size)) / 2);
+ return ASFloorPixelValue((crossSize - crossDimension(style.direction, item.layout.size)) / 2);
case ASStackLayoutAlignItemsBaselineFirst:
case ASStackLayoutAlignItemsBaselineLast:
- return baseline - ASStackUnpositionedLayout::baselineForItem(style, l);
+ return baseline - ASStackUnpositionedLayout::baselineForItem(style, item);
case ASStackLayoutAlignItemsStart:
case ASStackLayoutAlignItemsStretch:
case ASStackLayoutAlignItemsNotSet:
@@ -36,77 +38,149 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
}
}
-/**
- * Positions children according to the stack style and positioning properties.
- *
- * @param style The layout style of the overall stack layout
- * @param firstChildOffset Offset of the first child
- * @param extraSpacing Spacing between children, in addition to spacing set to the stack's layout style
- * @param unpositionedLayout Unpositioned children of the stack
- * @param constrainedSize Constrained size of the stack
- */
-static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style,
- const CGFloat firstChildOffset,
- const CGFloat extraSpacing,
- const ASStackUnpositionedLayout &unpositionedLayout,
- const ASSizeRange &constrainedSize)
+static void crossOffsetAndSpacingForEachLine(const std::size_t numOfLines,
+ const CGFloat crossViolation,
+ ASStackLayoutAlignContent alignContent,
+ CGFloat &offset,
+ CGFloat &spacing)
{
- CGFloat crossSize = unpositionedLayout.crossSize;
- CGFloat baseline = unpositionedLayout.baseline;
+ ASDisplayNodeCAssertTrue(numOfLines > 0);
- // Adjust the position of the unpositioned layouts to be positioned
- CGPoint p = directionPoint(style.direction, firstChildOffset, 0);
- BOOL first = YES;
- const auto stackedChildren = unpositionedLayout.items;
- for (const auto &l : stackedChildren) {
- p = p + directionPoint(style.direction, l.child.style.spacingBefore, 0);
- if (!first) {
- p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0);
- }
- first = NO;
- l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, baseline));
-
- p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.style.spacingAfter, 0);
+ // Handle edge cases
+ if (alignContent == ASStackLayoutAlignContentSpaceBetween && (crossViolation < kViolationEpsilon || numOfLines == 1)) {
+ alignContent = ASStackLayoutAlignContentStart;
+ } else if (alignContent == ASStackLayoutAlignContentSpaceAround && (crossViolation < kViolationEpsilon || numOfLines == 1)) {
+ alignContent = ASStackLayoutAlignContentCenter;
+ }
+
+ offset = 0;
+ spacing = 0;
+
+ switch (alignContent) {
+ case ASStackLayoutAlignContentCenter:
+ offset = crossViolation / 2;
+ break;
+ case ASStackLayoutAlignContentEnd:
+ offset = crossViolation;
+ break;
+ case ASStackLayoutAlignContentSpaceBetween:
+ // Spacing between the items, no spaces at the edges, evenly distributed
+ spacing = crossViolation / (numOfLines - 1);
+ break;
+ case ASStackLayoutAlignContentSpaceAround: {
+ // Spacing between items are twice the spacing on the edges
+ CGFloat spacingUnit = crossViolation / (numOfLines * 2);
+ offset = spacingUnit;
+ spacing = spacingUnit * 2;
+ break;
+ }
+ case ASStackLayoutAlignContentStart:
+ case ASStackLayoutAlignContentStretch:
+ break;
}
-
- return {std::move(stackedChildren)};
}
-ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout,
- const ASStackLayoutSpecStyle &style,
- const ASSizeRange &constrainedSize)
+static void stackOffsetAndSpacingForEachItem(const std::size_t numOfItems,
+ const CGFloat stackViolation,
+ ASStackLayoutJustifyContent justifyContent,
+ CGFloat &offset,
+ CGFloat &spacing)
{
- const auto numOfItems = unpositionedLayout.items.size();
ASDisplayNodeCAssertTrue(numOfItems > 0);
- const CGFloat violation = unpositionedLayout.violation;
- ASStackLayoutJustifyContent justifyContent = style.justifyContent;
- // Handle edge cases of "space between" and "space around"
- if (justifyContent == ASStackLayoutJustifyContentSpaceBetween && (violation < 0 || numOfItems == 1)) {
+ // Handle edge cases
+ if (justifyContent == ASStackLayoutJustifyContentSpaceBetween && (stackViolation < kViolationEpsilon || numOfItems == 1)) {
justifyContent = ASStackLayoutJustifyContentStart;
- } else if (justifyContent == ASStackLayoutJustifyContentSpaceAround && (violation < 0 || numOfItems == 1)) {
+ } else if (justifyContent == ASStackLayoutJustifyContentSpaceAround && (stackViolation < kViolationEpsilon || numOfItems == 1)) {
justifyContent = ASStackLayoutJustifyContentCenter;
}
+ offset = 0;
+ spacing = 0;
+
switch (justifyContent) {
- case ASStackLayoutJustifyContentStart: {
- return stackedLayout(style, 0, 0, unpositionedLayout, constrainedSize);
- }
- case ASStackLayoutJustifyContentCenter: {
- return stackedLayout(style, std::floor(violation / 2), 0, unpositionedLayout, constrainedSize);
- }
- case ASStackLayoutJustifyContentEnd: {
- return stackedLayout(style, violation, 0, unpositionedLayout, constrainedSize);
- }
- case ASStackLayoutJustifyContentSpaceBetween: {
+ case ASStackLayoutJustifyContentCenter:
+ offset = stackViolation / 2;
+ break;
+ case ASStackLayoutJustifyContentEnd:
+ offset = stackViolation;
+ break;
+ case ASStackLayoutJustifyContentSpaceBetween:
// Spacing between the items, no spaces at the edges, evenly distributed
- const auto numOfSpacings = numOfItems - 1;
- return stackedLayout(style, 0, violation / numOfSpacings, unpositionedLayout, constrainedSize);
- }
+ spacing = stackViolation / (numOfItems - 1);
+ break;
case ASStackLayoutJustifyContentSpaceAround: {
// Spacing between items are twice the spacing on the edges
- CGFloat spacingUnit = violation / (numOfItems * 2);
- return stackedLayout(style, spacingUnit, spacingUnit * 2, unpositionedLayout, constrainedSize);
+ CGFloat spacingUnit = stackViolation / (numOfItems * 2);
+ offset = spacingUnit;
+ spacing = spacingUnit * 2;
+ break;
}
+ case ASStackLayoutJustifyContentStart:
+ break;
}
}
+
+static void positionItemsInLine(const ASStackUnpositionedLine &line,
+ const ASStackLayoutSpecStyle &style,
+ const CGPoint &startingPoint,
+ const CGFloat stackSpacing)
+{
+ CGPoint p = startingPoint;
+ BOOL first = YES;
+
+ for (const auto &item : line.items) {
+ p = p + directionPoint(style.direction, item.child.style.spacingBefore, 0);
+ if (!first) {
+ p = p + directionPoint(style.direction, style.spacing + stackSpacing, 0);
+ }
+ first = NO;
+ item.layout.position = p + directionPoint(style.direction, 0, crossOffsetForItem(item, style, line.crossSize, line.baseline));
+
+ p = p + directionPoint(style.direction, stackDimension(style.direction, item.layout.size) + item.child.style.spacingAfter, 0);
+ }
+}
+
+ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &layout,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange)
+{
+ const auto &lines = layout.lines;
+ if (lines.empty()) {
+ return {};
+ }
+
+ const auto numOfLines = lines.size();
+ const auto direction = style.direction;
+ const auto alignContent = style.alignContent;
+ const auto justifyContent = style.justifyContent;
+ const auto crossViolation = ASStackUnpositionedLayout::computeCrossViolation(layout.crossDimensionSum, style, sizeRange);
+ CGFloat crossOffset;
+ CGFloat crossSpacing;
+ crossOffsetAndSpacingForEachLine(numOfLines, crossViolation, alignContent, crossOffset, crossSpacing);
+
+ std::vector positionedItems;
+ CGPoint p = directionPoint(direction, 0, crossOffset);
+ BOOL first = YES;
+ for (const auto &line : lines) {
+ if (!first) {
+ p = p + directionPoint(direction, 0, crossSpacing);
+ }
+ first = NO;
+
+ const auto &items = line.items;
+ const auto stackViolation = ASStackUnpositionedLayout::computeStackViolation(line.stackDimensionSum, style, sizeRange);
+ CGFloat stackOffset;
+ CGFloat stackSpacing;
+ stackOffsetAndSpacingForEachItem(items.size(), stackViolation, justifyContent, stackOffset, stackSpacing);
+
+ setStackValueToPoint(direction, stackOffset, p);
+ positionItemsInLine(line, style, p, stackSpacing);
+ std::move(items.begin(), items.end(), std::back_inserter(positionedItems));
+
+ p = p + directionPoint(direction, -stackOffset, line.crossSize);
+ }
+
+ const CGSize finalSize = directionSize(direction, layout.stackDimensionSum, layout.crossDimensionSum);
+ return {std::move(positionedItems), ASSizeRangeClamp(sizeRange, finalSize)};
+}
diff --git a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h
index af7bb8d0cd..e98852b948 100644
--- a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h
+++ b/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h
@@ -14,6 +14,9 @@
#import
#import
+/** The threshold that determines if a violation has actually occurred. */
+extern CGFloat const kViolationEpsilon;
+
struct ASStackLayoutSpecChild {
/** The original source child. */
id element;
@@ -30,19 +33,27 @@ struct ASStackLayoutSpecItem {
ASLayout *layout;
};
+struct ASStackUnpositionedLine {
+ /** The set of proposed children in this line, each contains child layout, not yet positioned. */
+ std::vector items;
+ /** The total size of the children in the stack dimension, including all spacing. */
+ CGFloat stackDimensionSum;
+ /** The size in the cross dimension */
+ CGFloat crossSize;
+ /** The baseline of the stack which baseline aligned children should align to */
+ CGFloat baseline;
+};
/** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */
struct ASStackUnpositionedLayout {
- /** A set of proposed child layouts, not yet positioned. */
- const std::vector items;
- /** The total size of the children in the stack dimension, including all spacing. */
+ /** The set of proposed lines, each contains child layouts, not yet positioned. */
+ const std::vector lines;
+ /**
+ * In a single line stack (e.g no wrao), this is the total size of the children in the stack dimension, including all spacing.
+ * In a multi-line stack, this is the largest stack dimension among lines.
+ */
const CGFloat stackDimensionSum;
- /** The amount by which stackDimensionSum violates constraints. If positive, less than min; negative, greater than max. */
- const CGFloat violation;
- /** The size in the cross dimension */
- const CGFloat crossSize;
- /** The baseline of the stack which baseline aligned children should align to */
- const CGFloat baseline;
+ const CGFloat crossDimensionSum;
/** Given a set of children, computes the unpositioned layouts for those children. */
static ASStackUnpositionedLayout compute(const std::vector &children,
@@ -51,4 +62,12 @@ struct ASStackUnpositionedLayout {
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
const ASStackLayoutSpecItem &l);
+
+ static CGFloat computeStackViolation(const CGFloat stackDimensionSum,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange);
+
+ static CGFloat computeCrossViolation(const CGFloat crossDimensionSum,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange);
};
diff --git a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm
index 8b7e06d4df..4ec2568591 100644
--- a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm
+++ b/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm
@@ -16,6 +16,8 @@
#import
#import
+CGFloat const kViolationEpsilon = 0.01;
+
static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style,
const ASStackLayoutSpecChild &child,
const CGFloat stackMax,
@@ -65,8 +67,76 @@ static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child,
return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}];
}
-/** The threshold that determines if a violation has actually occurred. */
-static const CGFloat kViolationEpsilon = 0.01;
+/**
+ Computes the consumed cross dimension length for the given vector of lines and stacking style.
+
+ Cross Dimension
+ +--------------------->
+ +--------+ +--------+ +--------+ +---------+
+ Vertical |Vertical| |Vertical| |Vertical| |Vertical |
+ Stack | Line 1 | | Line 2 | | Line 3 | | Line 4 |
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +---------+
+ crossDimensionSum
+ |------------------------------------------|
+
+ @param lines unpositioned lines
+ */
+static CGFloat computeLinesCrossDimensionSum(const std::vector &lines)
+{
+ return std::accumulate(lines.begin(), lines.end(), 0.0,
+ [&](CGFloat x, const ASStackUnpositionedLine &l) {
+ return x + l.crossSize;
+ });
+}
+
+
+/**
+ Computes the violation by comparing a cross dimension sum with the overall allowable size range for the stack.
+
+ Violation is the distance you would have to add to the unbounded cross-direction length of the stack spec's
+ lines in order to bring the stack within its allowed sizeRange. The diagram below shows 3 vertical stacks, each contains 3-5 vertical lines,
+ with the different types of violation.
+
+ Cross Dimension
+ +--------------------->
+ cross size range
+ |------------|
+ +--------+ +--------+ +--------+ +---------+ - - - - - - - -
+ Vertical |Vertical| |Vertical| |Vertical| |Vertical | | ^
+ Stack 1 | Line 1 | | Line 2 | | Line 3 | | Line 4 | (zero violation) | stack size range
+ | | | | | | | | | | v
+ +--------+ +--------+ +--------+ +---------+ - - - - - - - -
+ | |
+ +--------+ +--------+ +--------+ - - - - - - - - - - - -
+ Vertical | | | | | | | | ^
+ Stack 2 | | | | | |<--> (positive violation) | stack size range
+ | | | | | | | | v
+ +--------+ +--------+ +--------+ - - - - - - - - - - - -
+ | |<------> (negative violation)
+ +--------+ +--------+ +--------+ +---------+ +-----------+ - - -
+ Vertical | | | | | | | | | | | | ^
+ Stack 3 | | | | | | | | | | | stack size range
+ | | | | | | | | | | | | v
+ +--------+ +--------+ +--------+ +---------+ +-----------+ - - -
+
+ @param crossDimensionSum the consumed length of the lines in the stack along the cross dimension
+ @param style layout style to be applied to all children
+ @param sizeRange the range of allowable sizes for the stack layout spec
+ */
+CGFloat ASStackUnpositionedLayout::computeCrossViolation(const CGFloat crossDimensionSum,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange)
+{
+ const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
+ const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
+ if (crossDimensionSum < minCrossDimension) {
+ return minCrossDimension - crossDimensionSum;
+ } else if (crossDimensionSum > maxCrossDimension) {
+ return maxCrossDimension - crossDimensionSum;
+ }
+ return 0;
+}
/**
Stretches children to lay out along the cross axis according to the alignment stretch settings of the children
@@ -97,13 +167,13 @@ static const CGFloat kViolationEpsilon = 0.01;
|
+--------------------------------------------------+ + crossMax
- @param items pre-computed child layouts; modified in-place as needed
+ @param items pre-computed items; modified in-place as needed
@param style the layout style of the overall stack layout
*/
-static void stretchChildrenAlongCrossDimension(std::vector &items,
- const ASStackLayoutSpecStyle &style,
- const CGSize parentSize,
- const CGFloat crossSize)
+static void stretchItemsAlongCrossDimension(std::vector &items,
+ const ASStackLayoutSpecStyle &style,
+ const CGSize parentSize,
+ const CGFloat crossSize)
{
for (auto &item : items) {
const ASStackLayoutAlignItems alignItems = alignment(item.child.style.alignSelf, style.alignItems);
@@ -122,6 +192,33 @@ static void stretchChildrenAlongCrossDimension(std::vector &lines,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange,
+ const CGSize parentSize)
+{
+ ASDisplayNodeCAssertFalse(lines.empty());
+ const std::size_t numOfLines = lines.size();
+ const CGFloat violation = ASStackUnpositionedLayout::computeCrossViolation(computeLinesCrossDimensionSum(lines), style, sizeRange);
+ // Don't stretch if the stack is single line, because the line's cross size was clamped against the stack's constrained size.
+ const BOOL shouldStretchLines = (numOfLines > 1
+ && style.alignContent == ASStackLayoutAlignContentStretch
+ && violation > kViolationEpsilon);
+
+ CGFloat extraCrossSizePerLine = violation / numOfLines;
+ for (auto &line : lines) {
+ if (shouldStretchLines) {
+ line.crossSize += extraCrossSizePerLine;
+ }
+
+ stretchItemsAlongCrossDimension(line.items, style, parentSize, line.crossSize);
+ }
+}
static BOOL itemIsBaselineAligned(const ASStackLayoutSpecStyle &style,
const ASStackLayoutSpecItem &l)
@@ -131,20 +228,20 @@ static BOOL itemIsBaselineAligned(const ASStackLayoutSpecStyle &style,
}
CGFloat ASStackUnpositionedLayout::baselineForItem(const ASStackLayoutSpecStyle &style,
- const ASStackLayoutSpecItem &l)
+ const ASStackLayoutSpecItem &item)
{
- switch (alignment(l.child.style.alignSelf, style.alignItems)) {
+ switch (alignment(item.child.style.alignSelf, style.alignItems)) {
case ASStackLayoutAlignItemsBaselineFirst:
- return l.child.style.ascender;
+ return item.child.style.ascender;
case ASStackLayoutAlignItemsBaselineLast:
- return crossDimension(style.direction, l.layout.size) + l.child.style.descender;
+ return crossDimension(style.direction, item.layout.size) + item.child.style.descender;
default:
return 0;
}
}
/**
- * Finds cross dimension size and baseline of the stack.
+ * Computes cross size and baseline of each line.
* https://www.w3.org/TR/css-flexbox-1/#algo-cross-line
*
* @param items All items to lay out
@@ -153,41 +250,64 @@ CGFloat ASStackUnpositionedLayout::baselineForItem(const ASStackLayoutSpecStyle
* @param crossSize result of the cross size
* @param baseline result of the stack baseline
*/
-static void computeCrossSizeAndBaseline(const std::vector &items,
- const ASStackLayoutSpecStyle &style,
- const ASSizeRange &sizeRange,
- CGFloat &crossSize,
- CGFloat &baseline)
+static void computeLinesCrossSizeAndBaseline(std::vector &lines,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange)
{
+ ASDisplayNodeCAssertFalse(lines.empty());
+ const BOOL isSingleLine = (lines.size() == 1);
+
const auto minCrossSize = crossDimension(style.direction, sizeRange.min);
const auto maxCrossSize = crossDimension(style.direction, sizeRange.max);
+ const BOOL definiteCrossSize = (minCrossSize == maxCrossSize);
- // Step 1. Collect all the flex items whose align-self is baseline. Find the largest of the distances
- // between each item’s baseline and its hypothetical outer cross-start edge (aka. its ascender value),
- // and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge
- // (aka. the opposite of its descender value, because a negative descender means the item extends below its baseline),
- // and sum these two values.
- //
- // Step 2. Find the maximum cross dimension size among child layouts.
- CGFloat maxStartToBaselineDistance = 0;
- CGFloat maxBaselineToEndDistance = 0;
- CGFloat maxItemCrossSize = 0;
- for (const auto &item : items) {
- if (itemIsBaselineAligned(style, item)) {
- CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item);
- maxStartToBaselineDistance = MAX(maxStartToBaselineDistance, baseline);
- maxBaselineToEndDistance = MAX(maxBaselineToEndDistance, crossDimension(style.direction, item.layout.size) - baseline);
- } else {
- maxItemCrossSize = MAX(maxItemCrossSize, crossDimension(style.direction, item.layout.size));
+ // If the stack is single-line and has a definite cross size, the cross size of the line is the stack's definite cross size.
+ if (isSingleLine && definiteCrossSize) {
+ auto &line = lines[0];
+ line.crossSize = minCrossSize;
+
+ // We still need to determine the line's baseline
+ //TODO unit test
+ for (const auto &item : line.items) {
+ if (itemIsBaselineAligned(style, item)) {
+ CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item);
+ line.baseline = MAX(line.baseline, baseline);
+ }
}
+
+ return;
}
- // Step 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero.
- crossSize = MAX(maxStartToBaselineDistance + maxBaselineToEndDistance, maxItemCrossSize);
- // Clamp the cross-size to be within the stack's min and max cross-size properties.
- crossSize = MIN(MAX(minCrossSize, crossSize), maxCrossSize);
-
- baseline = maxStartToBaselineDistance;
+ for (auto &line : lines) {
+ const auto &items = line.items;
+ CGFloat maxStartToBaselineDistance = 0;
+ CGFloat maxBaselineToEndDistance = 0;
+ CGFloat maxItemCrossSize = 0;
+
+ for (const auto &item : items) {
+ if (itemIsBaselineAligned(style, item)) {
+ // Step 1. Collect all the items whose align-self is baseline. Find the largest of the distances
+ // between each item’s baseline and its hypothetical outer cross-start edge (aka. its baseline value),
+ // and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge,
+ // and sum these two values.
+ CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item);
+ maxStartToBaselineDistance = MAX(maxStartToBaselineDistance, baseline);
+ maxBaselineToEndDistance = MAX(maxBaselineToEndDistance, crossDimension(style.direction, item.layout.size) - baseline);
+ } else {
+ // Step 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size.
+ maxItemCrossSize = MAX(maxItemCrossSize, crossDimension(style.direction, item.layout.size));
+ }
+ }
+
+ // Step 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero.
+ line.crossSize = MAX(maxStartToBaselineDistance + maxBaselineToEndDistance, maxItemCrossSize);
+ if (isSingleLine) {
+ // If the stack is single-line, then clamp the line’s cross-size to be within the stack's min and max cross-size properties.
+ line.crossSize = MIN(MAX(minCrossSize, line.crossSize), maxCrossSize);
+ }
+
+ line.baseline = maxStartToBaselineDistance;
+ }
}
/**
@@ -314,8 +434,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector
@param items unpositioned layouts for items
@param style the layout style of the overall stack layout
*/
-static CGFloat computeStackDimensionSum(const std::vector &items,
- const ASStackLayoutSpecStyle &style)
+static CGFloat computeItemsStackDimensionSum(const std::vector &items,
+ const ASStackLayoutSpecStyle &style)
{
// Sum up the childrens' spacing
const CGFloat childSpacingSum = std::accumulate(items.begin(), items.end(),
@@ -333,6 +453,7 @@ static CGFloat computeStackDimensionSum(const std::vector
return childStackDimensionSum;
}
+//TODO move this up near computeCrossViolation and make both methods share the same code path, to make sure they share the same concept of "negative" and "positive" violations.
/**
Computes the violation by comparing a stack dimension sum with the overall allowable size range for the stack.
@@ -364,9 +485,9 @@ static CGFloat computeStackDimensionSum(const std::vector
@param style layout style to be applied to all children
@param sizeRange the range of allowable sizes for the stack layout spec
*/
-static CGFloat computeViolation(const CGFloat stackDimensionSum,
- const ASStackLayoutSpecStyle &style,
- const ASSizeRange &sizeRange)
+CGFloat ASStackUnpositionedLayout::computeStackViolation(const CGFloat stackDimensionSum,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange)
{
const CGFloat minStackDimension = stackDimension(style.direction, sizeRange.min);
const CGFloat maxStackDimension = stackDimension(style.direction, sizeRange.max);
@@ -394,65 +515,106 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector &items,
- const ASStackLayoutSpecStyle &style,
- const ASSizeRange &sizeRange,
- const CGSize parentSize,
- const BOOL useOptimizedFlexing)
+static void flexLinesAlongStackDimension(std::vector &lines,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange,
+ const CGSize parentSize,
+ const BOOL useOptimizedFlexing)
{
- 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.0, [&](CGFloat x, const ASStackLayoutSpecItem &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, parentSize);
+ for (auto &line : lines) {
+ auto &items = line.items;
+ const CGFloat violation = ASStackUnpositionedLayout::computeStackViolation(computeItemsStackDimensionSum(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.0, [&](CGFloat x, const ASStackLayoutSpecItem &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, parentSize);
+ }
+ return;
+ }
+ std::function flexAdjustment = flexAdjustmentInViolationDirection(items,
+ style,
+ violation,
+ flexFactorSum);
+
+ // Compute any remaining violation to the first flexible child.
+ const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
+ return x - flexAdjustment(item);
+ });
+ BOOL isFirstFlex = YES;
+ for (ASStackLayoutSpecItem &item : items) {
+ const CGFloat currentFlexAdjustment = flexAdjustment(item);
+ // 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);
+ // Only apply the remaining violation for the first flexible child that has a flex grow factor.
+ const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0);
+ item.layout = crossChildLayout(item.child,
+ style,
+ MAX(flexedStackSize, 0),
+ MAX(flexedStackSize, 0),
+ crossDimension(style.direction, sizeRange.min),
+ crossDimension(style.direction, sizeRange.max),
+ parentSize);
+ isFirstFlex = NO;
+ }
}
- return;
}
- std::function flexAdjustment = flexAdjustmentInViolationDirection(items,
- style,
- violation,
- flexFactorSum);
+}
- // Compute any remaining violation to the first flexible child.
- const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) {
- return x - flexAdjustment(item);
- });
- BOOL isFirstFlex = YES;
- for (ASStackLayoutSpecItem &item : items) {
- const CGFloat currentFlexAdjustment = flexAdjustment(item);
- // 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);
- // Only apply the remaining violation for the first flexible child that has a flex grow factor.
- const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0);
- item.layout = crossChildLayout(item.child,
- style,
- MAX(flexedStackSize, 0),
- MAX(flexedStackSize, 0),
- crossDimension(style.direction, sizeRange.min),
- crossDimension(style.direction, sizeRange.max),
- parentSize);
- isFirstFlex = NO;
- }
+/**
+ https://www.w3.org/TR/css-flexbox-1/#algo-line-break
+ */
+static std::vector collectChildrenIntoLines(const std::vector &items,
+ const ASStackLayoutSpecStyle &style,
+ const ASSizeRange &sizeRange)
+{
+ //TODO if infinite max stack size, fast path
+ if (style.flexWrap == ASStackLayoutFlexWrapNoWrap) {
+ return std::vector (1, {.items = std::move(items)});
}
+
+ std::vector lines;
+ std::vector lineItems;
+ CGFloat lineStackDimensionSum = 0;
+
+ for(auto it = items.begin(); it != items.end(); ++it) {
+ const auto &item = *it;
+ const CGFloat itemStackDimension = stackDimension(style.direction, item.layout.size);
+ const BOOL negativeViolationIfAddItem = (ASStackUnpositionedLayout::computeStackViolation(lineStackDimensionSum + itemStackDimension, style, sizeRange) < 0);
+ const BOOL breakCurrentLine = negativeViolationIfAddItem && !lineItems.empty();
+
+ if (breakCurrentLine) {
+ lines.push_back({.items = std::vector (lineItems)});
+ lineItems.clear();
+ lineStackDimensionSum = 0;
+ }
+
+ lineItems.push_back(std::move(item));
+ lineStackDimensionSum += itemStackDimension;
+ }
+
+ // Handle last line
+ lines.push_back({.items = std::vector (lineItems)});
+
+ return lines;
}
/**
@@ -507,25 +669,34 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector items = layoutChildrenAlongUnconstrainedStackDimension(children,
- style,
- sizeRange,
- parentSize,
- optimizedFlexing);
+ style,
+ sizeRange,
+ parentSize,
+ optimizedFlexing);
- // Resolve the flexible lengths (https://www.w3.org/TR/css-flexbox-1/#algo-flex)
- // Determine the hypothetical cross size of each item (https://www.w3.org/TR/css-flexbox-1/#algo-cross-item)
- flexChildrenAlongStackDimension(items, style, sizeRange, parentSize, optimizedFlexing);
+ // Collect items into lines (https://www.w3.org/TR/css-flexbox-1/#algo-line-break)
+ std::vector lines = collectChildrenIntoLines(items, style, sizeRange);
- // Step 4. Cross Size Determination (https://www.w3.org/TR/css-flexbox-1/#cross-sizing)
- //
- // Calculate the cross size of the stack (https://www.w3.org/TR/css-flexbox-1/#algo-cross-line)
- CGFloat crossSize;
- CGFloat baseline;
- computeCrossSizeAndBaseline(items, style, sizeRange, crossSize, baseline);
+ // Resolve the flexible lengths (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths)
+ flexLinesAlongStackDimension(lines, style, sizeRange, parentSize, optimizedFlexing);
+
+ // Calculate the cross size of each flex line (https://www.w3.org/TR/css-flexbox-1/#algo-cross-line)
+ computeLinesCrossSizeAndBaseline(lines, style, sizeRange);
+
+ // Handle 'align-content: stretch' (https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch)
// Determine the used cross size of each item (https://www.w3.org/TR/css-flexbox-1/#algo-stretch)
- // If the flex item has stretch alignment, redo layout
- stretchChildrenAlongCrossDimension(items, style, parentSize, crossSize);
+ stretchLinesAlongCrossDimension(lines, style, sizeRange, parentSize);
- const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
- return {std::move(items), stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange), crossSize, baseline};
+ // Compute stack dimension sum of each line and the whole stack
+ CGFloat layoutStackDimensionSum = 0;
+ for (auto &line : lines) {
+ line.stackDimensionSum = computeItemsStackDimensionSum(line.items, style);
+ // layoutStackDimensionSum is the max stackDimensionSum among all lines
+ layoutStackDimensionSum = MAX(line.stackDimensionSum, layoutStackDimensionSum);
+ }
+ // Compute cross dimension sum of the stack.
+ // This should be done before `lines` are moved to a new ASStackUnpositionedLayout struct (i.e `std::move(lines)`)
+ CGFloat layoutCrossDimensionSum = computeLinesCrossDimensionSum(lines);
+
+ return {.lines = std::move(lines), .stackDimensionSum = layoutStackDimensionSum, .crossDimensionSum = layoutCrossDimensionSum};
}
diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm
index f0438e6496..a21a37a83b 100644
--- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm
+++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm
@@ -22,15 +22,6 @@
@implementation ASStackLayoutSpecSnapshotTests
-#pragma mark - XCTestCase
-
-- (void)setUp
-{
- [super setUp];
-
- self.recordMode = NO;
-}
-
#pragma mark - Utility methods
static NSArray *defaultSubnodes()
@@ -110,6 +101,8 @@ static NSArray *defaultTextNodes()
spacing:style.spacing
justifyContent:style.justifyContent
alignItems:style.alignItems
+ flexWrap:style.flexWrap
+ alignContent:style.alignContent
children:children];
[self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
@@ -162,6 +155,28 @@ static NSArray *defaultTextNodes()
[self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:newSubnodes identifier:identifier];
}
+- (void)testStackLayoutSpecWithAlignContent:(ASStackLayoutAlignContent)alignContent
+ sizeRange:(ASSizeRange)sizeRange
+ identifier:(NSString *)identifier
+{
+ ASStackLayoutSpecStyle style = {
+ .direction = ASStackLayoutDirectionHorizontal,
+ .flexWrap = ASStackLayoutFlexWrapWrap,
+ .alignContent = alignContent,
+ };
+
+ CGSize subnodeSize = {50, 50};
+ NSArray *subnodes = @[
+ ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor yellowColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor magentaColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor cyanColor], subnodeSize),
+ ];
+
+ [self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier];
+}
#pragma mark -
@@ -1167,4 +1182,77 @@ static NSArray *defaultTextNodes()
[self testStackLayoutSpec:stackLayoutSpec sizeRange:kSize subnodes:children identifier:nil];
}
+#pragma mark - Content alignment tests
+
+- (void)testAlignContentUnderflow
+{
+ // 3 lines, each line has 2 items, each item has a size of {50, 50}
+ // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines
+ static ASSizeRange kSize = {{110, 300}, {110, 300}};
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:@"alignContentStart"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:@"alignContentCenter"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:@"alignContentEnd"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:@"alignContentSpaceBetween"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:@"alignContentSpaceAround"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStretch sizeRange:kSize identifier:@"alignContentStretch"];
+}
+
+- (void)testAlignContentOverflow
+{
+ // 6 lines, each line has 1 item, each item has a size of {50, 50}
+ // width is 40px. It's 10px smaller than the width of each item (40px vs 50px) to test that items are still correctly collected into lines
+ static ASSizeRange kSize = {{40, 260}, {40, 260}};
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:@"alignContentStart"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:@"alignContentCenter"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:@"alignContentEnd"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:@"alignContentSpaceBetween"];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:@"alignContentSpaceAround"];
+}
+
+- (void)testAlignContentWithUnconstrainedCrossSize
+{
+ // 3 lines, each line has 2 items, each item has a size of {50, 50}
+ // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines
+ // height is unconstrained. It causes no cross size violation and the end results are all similar to ASStackLayoutAlignContentStart.
+ static ASSizeRange kSize = {{110, 0}, {110, CGFLOAT_MAX}};
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:nil];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:nil];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:nil];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:nil];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:nil];
+ [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStretch sizeRange:kSize identifier:nil];
+}
+
+- (void)testAlignContentStretchAndOtherAlignments
+{
+ ASStackLayoutSpecStyle style = {
+ .direction = ASStackLayoutDirectionHorizontal,
+ .flexWrap = ASStackLayoutFlexWrapWrap,
+ .alignContent = ASStackLayoutAlignContentStretch,
+ .alignItems = ASStackLayoutAlignItemsStart,
+ };
+
+ CGSize subnodeSize = {50, 50};
+ NSArray *subnodes = @[
+ // 1st line
+ ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor yellowColor], subnodeSize),
+ // 2nd line
+ ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor magentaColor], subnodeSize),
+ // 3rd line
+ ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize),
+ ASDisplayNodeWithBackgroundColor([UIColor cyanColor], subnodeSize),
+ ];
+
+ subnodes[1].style.alignSelf = ASStackLayoutAlignSelfStart;
+ subnodes[3].style.alignSelf = ASStackLayoutAlignSelfCenter;
+ subnodes[5].style.alignSelf = ASStackLayoutAlignSelfEnd;
+
+ // 3 lines, each line has 2 items, each item has a size of {50, 50}
+ // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines
+ static ASSizeRange kSize = {{110, 300}, {110, 300}};
+ [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
+}
+
@end
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png
new file mode 100644
index 0000000000..cee1ed8cf7
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png
new file mode 100644
index 0000000000..d6736f9153
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png
new file mode 100644
index 0000000000..cee1ed8cf7
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png
new file mode 100644
index 0000000000..1e32492bac
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png
new file mode 100644
index 0000000000..1e32492bac
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png
new file mode 100644
index 0000000000..c091f22768
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png
new file mode 100644
index 0000000000..888442daec
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png
new file mode 100644
index 0000000000..ecc727a501
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png
new file mode 100644
index 0000000000..b8ef3d2aee
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png
new file mode 100644
index 0000000000..d7907975ae
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png
new file mode 100644
index 0000000000..032e9fac3a
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png
new file mode 100644
index 0000000000..6d08d18e4a
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png
new file mode 100644
index 0000000000..032e9fac3a
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png
new file mode 100644
index 0000000000..4df2a81184
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png
new file mode 100644
index 0000000000..4df2a81184
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png
new file mode 100644
index 0000000000..51d060062c
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png
new file mode 100644
index 0000000000..fea7203a26
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png
new file mode 100644
index 0000000000..9e0bdad429
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png
new file mode 100644
index 0000000000..bb608622fd
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png
new file mode 100644
index 0000000000..17b8dbfbc4
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png
new file mode 100644
index 0000000000..ab969dd638
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png
new file mode 100644
index 0000000000..584aca77db
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png differ
diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png
new file mode 100644
index 0000000000..12936781c0
Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png differ