From 499adb514566fd8917febf94461cb6d9803d4ea7 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:04:06 -0700 Subject: [PATCH 01/12] Added method to ASLayoutable to allow a layoutable to override how it will be added to the layoutSpec. Also moved ASStaticLayoutSpec to act more like the other layouts. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 +++ AsyncDisplayKit/ASDisplayNode.mm | 5 ++ .../Layout/ASBackgroundLayoutSpec.mm | 25 ++++------ AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h | 3 -- .../Layout/ASBaselineLayoutSpec.mm | 28 ++++++----- AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASLayoutSpec.h | 9 ++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 50 +++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutable.h | 13 +++++ AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 28 +++++------ AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 3 -- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 45 +++++++++-------- AsyncDisplayKit/Layout/ASStaticLayoutSpec.h | 41 +-------------- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 47 ++++++----------- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 24 +++++++++ 17 files changed, 191 insertions(+), 169 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStaticLayoutable.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 0809fbf515..38e68769dc 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -229,6 +229,8 @@ 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -574,6 +576,7 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -985,6 +988,7 @@ AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, @@ -1038,6 +1042,7 @@ ACF6ED201B17843500DA7C62 /* ASDimension.h in Headers */, ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */, ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */, + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */, ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, @@ -1152,6 +1157,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */, 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index ab1a644091..f06fb638bb 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -659,6 +659,11 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible return NO; } +- (id)finalLayoutable +{ + return self; +} + /** * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index fd3cfce134..ea18aca990 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kBackgroundChildKey = @"kBackgroundChildKey"; + @interface ASBackgroundLayoutSpec () -{ - id _child; - id _background; -} @end @implementation ASBackgroundLayoutSpec @@ -30,12 +28,11 @@ } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _child = child; - _background = background; + [self setChild:child]; + self.background = background; return self; } - + (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background; { return [[self alloc] initWithChild:child background:background]; @@ -46,12 +43,12 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2]; - if (_background) { + if (self.background) { // Size background to exactly the same size. - ASLayout *backgroundLayout = [_background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; backgroundLayout.position = CGPointZero; [sublayouts addObject:backgroundLayout]; } @@ -63,14 +60,12 @@ - (void)setBackground:(id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _background = background; + [super setChild:background forIdentifier:kBackgroundChildKey]; } -- (void)setChild:(id)child +- (id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; + return [super childForIdentifier:kBackgroundChildKey]; } @end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 8b784a56c3..46587d996b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -42,9 +42,6 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** The type of baseline alignment */ @property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - /** @param direction The direction of the stack view (horizontal or vertical) @param spacing The spacing between the children diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 670c69c991..45fa687f8c 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -27,7 +27,6 @@ @implementation ASBaselineLayoutSpec { - std::vector> _children; ASDN::RecursiveMutex _propertyLock; } @@ -47,10 +46,7 @@ _justifyContent = justifyContent; _baselineAlignment = baselineAlignment; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } @@ -65,7 +61,12 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, stackStyle, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); @@ -82,16 +83,19 @@ sublayouts:sublayouts]; } -- (void)addChild:(id)child +- (void)setChildren:(NSArray *)children { - _children.push_back(child); + [super setChildren:children]; +#if DEBUG + for (id child in children) { + NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif } -- (void)addChildren:(NSArray *)children +- (void)setChild:(id)child forIdentifier:(NSString *)identifier { - for (id child in children) { - [self addChild:child]; - } + ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); } @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index 1830895320..58dc55f962 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -17,7 +17,6 @@ { ASCenterLayoutSpecCenteringOptions _centeringOptions; ASCenterLayoutSpecSizingOptions _sizingOptions; - id _child; } - (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions @@ -30,7 +29,7 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _centeringOptions = centeringOptions; _sizingOptions = sizingOptions; - _child = child; + [self setChild:child]; return self; } @@ -41,12 +40,6 @@ return [[self alloc] initWithCenteringOptions:centeringOptions sizingOptions:sizingOptions child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -71,7 +64,7 @@ (_centeringOptions & ASCenterLayoutSpecCenteringX) != 0 ? 0 : constrainedSize.min.width, (_centeringOptions & ASCenterLayoutSpecCenteringY) != 0 ? 0 : constrainedSize.min.height, }; - ASLayout *sublayout = [_child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; + ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; // If we have an undetermined height or width, use the child size to define the layout // size diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 5ca96ab848..cc6ec9411a 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -19,7 +19,6 @@ @interface ASInsetLayoutSpec () { UIEdgeInsets _insets; - id _child; } @end @@ -50,7 +49,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _insets = insets; - _child = child; + [self setChild:child]; return self; } @@ -59,12 +58,6 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) return [[self alloc] initWithInsets:insets child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setInsets:(UIEdgeInsets)insets { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -95,7 +88,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) MAX(0, constrainedSize.max.height - insetsY), } }; - ASLayout *sublayout = [_child measureWithSizeRange:insetConstrainedSize]; + ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize]; const CGSize computedSize = ASSizeRangeClamp(constrainedSize, { finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width), diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index bb200cbfe1..f599c84813 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -22,4 +22,13 @@ - (instancetype)init; +- (void)setChild:(id)child; +- (id)child; + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier; +- (id)childForIdentifier:(NSString *)identifier; + +- (void)setChildren:(NSArray *)children; +- (NSArray *)children; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index ef09aa365e..58ecd114e9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,13 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +static NSString * const kDefaultChildKey = @"kDefaultChildKey"; +static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; + +@interface ASLayoutSpec() +@property (nonatomic, strong) NSMutableDictionary *layoutChildren; +@end + @implementation ASLayoutSpec @synthesize spacingBefore = _spacingBefore; @@ -24,12 +31,14 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize layoutChildren = _layoutChildren; - (instancetype)init { if (!(self = [super init])) { return nil; } + _layoutChildren = [NSMutableDictionary dictionary]; _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; @@ -42,4 +51,45 @@ return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } +- (id)finalLayoutable +{ + return self; +} + +- (void)setChild:(id)child; +{ + [self setChild:child forIdentifier:kDefaultChildKey]; +} + +- (id)child +{ + return self.layoutChildren[kDefaultChildKey]; +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + self.layoutChildren[identifier] = [child finalLayoutable]; +} + +- (id)childForIdentifier:(NSString *)identifier +{ + return self.layoutChildren[identifier]; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + [finalChildren addObject:[child finalLayoutable]]; + } + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; +} + +- (NSArray *)children +{ + return self.layoutChildren[kDefaultChildrenKey]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ac01ee05f..4ff1ee030a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -12,6 +12,7 @@ #import @class ASLayout; +@class ASLayoutSpec; /** * The ASLayoutable protocol declares a method for measuring the layout of an object. A class must implement the method @@ -29,4 +30,16 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; +/** + @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before + being added to a ASLayoutSpec. + + For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement + layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method + a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. + + It is rare that a class will need to implement this method. + */ +- (id)finalLayoutable; + @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index f4f025573b..05c1c0c4ca 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kOverlayChildKey = @"kOverlayChildKey"; + @implementation ASOverlayLayoutSpec -{ - id _overlay; - id _child; -} - (instancetype)initWithChild:(id)child overlay:(id)overlay { @@ -26,8 +24,8 @@ return nil; } ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - _overlay = overlay; - _child = child; + self.overlay = overlay; + [self setChild:child]; return self; } @@ -36,16 +34,14 @@ return [[self alloc] initWithChild:child overlay:overlay]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setOverlay:(id)overlay { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _overlay = overlay; + [super setChild:overlay forIdentifier:kOverlayChildKey]; +} + +- (id)overlay +{ + return [super childForIdentifier:kOverlayChildKey]; } /** @@ -56,8 +52,8 @@ ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; - if (_overlay) { - ASLayout *overlayLayout = [_overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + if (self.overlay) { + ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; overlayLayout.position = CGPointZero; [sublayouts addObject:overlayLayout]; } diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 5e5c6f0c66..64e9e2c118 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -22,7 +22,6 @@ @implementation ASRatioLayoutSpec { CGFloat _ratio; - id _child; } + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child @@ -38,16 +37,10 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); _ratio = ratio; - _child = child; + [self setChild:child]; return self; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setRatio:(CGFloat)ratio { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -77,7 +70,7 @@ // If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through. const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize); - ASLayout *sublayout = [_child measureWithSizeRange:childRange]; + ASLayout *sublayout = [self.child measureWithSizeRange:childRange]; sublayout.position = CGPointZero; return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 7825a9b8fc..b5a126c5cb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -55,7 +55,4 @@ */ + (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 3149979dd6..07243f30cc 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -23,9 +23,6 @@ #import "ASThread.h" @implementation ASStackLayoutSpec -{ - std::vector> _children; -} - (instancetype)init { @@ -47,26 +44,10 @@ _spacing = spacing; _justifyContent = justifyContent; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } -- (void)addChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _children.push_back(child); -} - -- (void)addChildren:(NSArray *)children -{ - for (id child in children) { - [self addChild:child]; - } -} - - (void)setDirection:(ASStackLayoutDirection)direction { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -91,10 +72,32 @@ _spacing = spacing; } +- (void)setChildren:(NSArray *)children +{ + [super setChildren:children]; + +#if DEBUG + for (id child in children) { + ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); +} + - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, style, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 38ed2f06d6..8bd3f3d9f2 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -11,43 +11,6 @@ #import #import -/** - * An ASStaticLayoutSpecChild object wraps an ASLayoutable object and provides position and size information, - * to be used as a child of an ASStaticLayoutSpec. - */ -@interface ASStaticLayoutSpecChild : NSObject - -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) id layoutableObject; - -/** - If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. - */ -@property (nonatomic, readonly) ASRelativeSizeRange size; - -/** - * Initializer. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - * - * @param size The size range that this child's size is trstricted according to. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; - -/** - * Convenience initializer with default size is Unconstrained in both dimensions, which sets the child's min size to zero - * and max size to the maximum available space it can consume without overflowing the spec's bounds. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; - -@end - /** * A layout spec that positions children at fixed positions. * @@ -56,10 +19,8 @@ @interface ASStaticLayoutSpec : ASLayoutSpec /** - @param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild. + @param children Children to be positioned at fixed positions, each conforms to ASStaticLayoutable */ + (instancetype)staticLayoutSpecWithChildren:(NSArray *)children; -- (void)addChild:(ASStaticLayoutSpecChild *)child; - @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 61a6d1724e..38a119a79e 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -13,31 +13,9 @@ #import "ASLayoutSpecUtilities.h" #import "ASInternalHelpers.h" #import "ASLayout.h" - -@implementation ASStaticLayoutSpecChild - -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; -{ - ASStaticLayoutSpecChild *c = [[super alloc] init]; - if (c) { - c->_position = position; - c->_layoutableObject = layoutableObject; - c->_size = size; - } - return c; -} - -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject -{ - return [self staticLayoutChildWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained]; -} - -@end +#import "ASStaticLayoutable.h" @implementation ASStaticLayoutSpec -{ - NSArray *_children; -} + (instancetype)staticLayoutSpecWithChildren:(NSArray *)children { @@ -49,14 +27,19 @@ if (!(self = [super init])) { return nil; } - _children = children; + self.children = children; return self; } -- (void)addChild:(ASStaticLayoutSpecChild *)child +- (void)setChildren:(NSArray *)children { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _children = [_children arrayByAddingObject:child]; + [super setChildren:children]; + +#if DEBUG + for (id child in children) { + ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStaticLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStaticLayoutable)]), @"child must conform to ASStaticLayoutable"); + } +#endif } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize @@ -66,16 +49,16 @@ constrainedSize.max.height }; - NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:_children.count]; - for (ASStaticLayoutSpecChild *child in _children) { + NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; + for (id child in self.children) { CGSize autoMaxSize = { constrainedSize.max.width - child.position.x, constrainedSize.max.height - child.position.y }; - ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.size) + ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) - : ASRelativeSizeRangeResolve(child.size, size); - ASLayout *sublayout = [child.layoutableObject measureWithSizeRange:childConstraint]; + : ASRelativeSizeRangeResolve(child.sizeRange, size); + ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; sublayout.position = child.position; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h new file mode 100644 index 0000000000..5618fa6e9b --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import +#import + +@protocol ASStaticLayoutable + +/** + If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. + */ +@property (nonatomic, assign) ASRelativeSizeRange sizeRange; + +/** The position of this object within its parent spec. */ +@property (nonatomic, assign) CGPoint position; + +@end From 32e2f7f1ad35b08bfa01310c5387c64851281f0d Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:37:34 -0700 Subject: [PATCH 02/12] fix kittens example --- examples/Kittens/Sample/KittenNode.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index faf2cc9693..d1f49351a2 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -140,7 +140,7 @@ static const CGFloat kInnerPadding = 10.0f; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; stackSpec.spacing = kInnerPadding; - [stackSpec addChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; + [stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding); From afade854af67ab6860cc4d2edfc78a9a5f8672f3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 09:36:22 -0700 Subject: [PATCH 03/12] Moved ASLayoutable* properties into ASLayoutOptions class --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++ AsyncDisplayKit/ASDisplayNode.h | 4 +- AsyncDisplayKit/ASDisplayNode.mm | 7 - AsyncDisplayKit/ASTextNode.h | 3 +- AsyncDisplayKit/ASTextNode.mm | 7 - AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h | 4 +- .../Layout/ASBaselineLayoutSpec.mm | 25 +--- AsyncDisplayKit/Layout/ASBaselineLayoutable.h | 4 +- AsyncDisplayKit/Layout/ASLayoutOptions.h | 49 +++++++ AsyncDisplayKit/Layout/ASLayoutOptions.m | 120 ++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.h | 15 ++- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 80 +++++++++--- AsyncDisplayKit/Layout/ASLayoutable.h | 1 + AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 16 +-- AsyncDisplayKit/Layout/ASStackLayoutable.h | 5 +- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 24 +--- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 3 +- .../Private/ASBaselinePositionedLayout.mm | 29 +++-- .../Private/ASStackPositionedLayout.mm | 8 +- .../Private/ASStackUnpositionedLayout.h | 4 +- .../Private/ASStackUnpositionedLayout.mm | 33 +++-- .../ASCenterLayoutSpecSnapshotTests.mm | 2 +- .../ASStackLayoutSpecSnapshotTests.mm | 99 ++++++++------- Podfile.lock | 2 +- .../Kittens/Sample.xcodeproj/project.pbxproj | 2 + examples/Kittens/Sample/KittenNode.mm | 4 +- 26 files changed, 373 insertions(+), 189 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.h create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 38e68769dc..505370e6e5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -231,6 +231,10 @@ 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -577,6 +581,8 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; + 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -993,6 +999,8 @@ ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, + 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, ); name = Layout; path = ..; @@ -1060,6 +1068,7 @@ 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, 058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */, 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */, + 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */, 058D0A4B195D05CB00B7D73C /* ASDisplayNode.mm in Headers */, 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */, 058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */, @@ -1191,6 +1200,7 @@ B35062391B010EFD0018CF92 /* ASThread.h in Headers */, B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */, 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */, + 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */, 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */, B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */, B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */, @@ -1451,6 +1461,7 @@ ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, @@ -1577,6 +1588,7 @@ B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 974bbe1272..7fcfbd5999 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,7 +13,7 @@ #import #import -#import +#import /** * UIView creation block. Used to create the backing view of a new display node. @@ -40,7 +40,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index f06fb638bb..291bd16285 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -41,12 +41,6 @@ @implementation ASDisplayNode -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) @@ -155,7 +149,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } _methodOverrides = overrides; - _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; } diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 1b35441c81..e8d5b76154 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,7 +7,6 @@ */ #import -#import @protocol ASTextNodeDelegate; @@ -30,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @abstract Draws interactive rich text. @discussion Backed by TextKit. */ -@interface ASTextNode : ASControlNode +@interface ASTextNode : ASControlNode /** @abstract The attributed string to show. diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 7ca68f5177..68f4f1fdab 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,7 +17,6 @@ #import #import -#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -108,9 +107,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) UILongPressGestureRecognizer *_longPressGestureRecognizer; } -@synthesize ascender = _ascender; -@synthesize descender = _descender; - #pragma mark - NSObject - (instancetype)init @@ -359,9 +355,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); - - _ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - _descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 46587d996b..d147c76700 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -9,7 +9,7 @@ */ #import -#import +#import typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ @@ -29,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { If the spec is created with a vertical direction, a child's vertical spacing will be measured from its baseline instead of from the child's bounding box. */ -@interface ASBaselineLayoutSpec : ASLayoutSpec +@interface ASBaselineLayoutSpec : ASLayoutSpec /** Specifies the direction children are stacked in. */ @property (nonatomic, assign) ASStackLayoutDirection direction; diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 45fa687f8c..50139a548b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -9,7 +9,6 @@ */ #import "ASBaselineLayoutSpec.h" -#import "ASStackLayoutable.h" #import #import @@ -26,12 +25,6 @@ @implementation ASBaselineLayoutSpec -{ - ASDN::RecursiveMutex _propertyLock; -} - -@synthesize ascender = _ascender; -@synthesize descender = _descender; - (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { @@ -61,8 +54,8 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } @@ -74,25 +67,11 @@ NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; - ASDN::MutexLocker l(_propertyLock); - _ascender = baselinePositionedLayout.ascender; - _descender = baselinePositionedLayout.descender; - return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; -#if DEBUG - for (id child in children) { - NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h index 5e05029964..7e52b2c056 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h @@ -6,9 +6,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "ASStackLayoutable.h" +#import -@protocol ASBaselineLayoutable +@protocol ASBaselineLayoutable /** * @abstract The distance from the top of the layoutable object to its baseline diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h new file mode 100644 index 0000000000..0bdee1ad09 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -0,0 +1,49 @@ +// +// ASLayoutOptions.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@protocol ASLayoutable; + +#import +#import +#import + +@interface ASLayoutOptions : NSObject + +- (instancetype)initWithLayoutable:(id)layoutable; +- (void)setValuesFromLayoutable:(id)layoutable; + +@property (nonatomic, assign) BOOL isMutable; + +#if DEBUG +@property (nonatomic, assign) NSUInteger changeMonitor; +#endif + +#pragma mark - ASStackLayoutable + +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +#pragma mark - ASBaselineLayoutable + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +#pragma mark - ASStaticLayoutable + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint position; + +- (void)setupDefaults; + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m new file mode 100644 index 0000000000..6207ef0d07 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -0,0 +1,120 @@ +// +// ASLayoutOptions.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptions.h" + +#import +#import +#import "ASInternalHelpers.h" +#import + +@implementation ASLayoutOptions + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; +#if DEBUG + [self addObserver:self forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; +#endif + } + return self; +} + +#if DEBUG ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor +{ + NSMutableSet *keys = [NSMutableSet set]; + unsigned int count; + + objc_property_t *properties = class_copyPropertyList([self class], &count); + for (size_t i = 0; i < count; ++i) { + NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; + + if ([property isEqualToString: @"observableSelf"] == NO) { + [keys addObject: property]; + } + } + free(properties); + + return keys; +} + +#endif + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ +#if DEBUG + if ([keyPath isEqualToString:@"changeMonitor"]) { + ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); + } else +#endif + { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.position = self.position; + + return copy; +} + +#pragma mark - Defaults +- (void)setupDefaults +{ + _flexBasis = ASRelativeDimensionUnconstrained; + _spacingBefore = 0; + _spacingAfter = 0; + _flexGrow = NO; + _flexShrink = NO; + _alignSelf = ASStackLayoutAlignSelfAuto; + + _ascender = 0; + _descender = 0; + + _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + _position = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.position = displayNode.frame.origin; + } +} + + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index f599c84813..dc4f100bb5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,10 +8,10 @@ * */ -#import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -23,12 +23,15 @@ - (instancetype)init; - (void)setChild:(id)child; -- (id)child; - - (void)setChild:(id)child forIdentifier:(NSString *)identifier; -- (id)childForIdentifier:(NSString *)identifier; - - (void)setChildren:(NSArray *)children; + +- (id)child; +- (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child; ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 58ecd114e9..f277a01bc4 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import + static NSString * const kDefaultChildKey = @"kDefaultChildKey"; static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @@ -25,12 +27,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -39,7 +35,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return nil; } _layoutChildren = [NSMutableDictionary dictionary]; - _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; } @@ -61,15 +56,34 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } -- (id)child -{ - return self.layoutChildren[kDefaultChildKey]; -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - self.layoutChildren[identifier] = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + layoutOptions.isMutable = NO; + self.layoutChildren[identifier] = child; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + } + + [finalChildren addObject:finalLayoutable]; + } + + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; } - (id)childForIdentifier:(NSString *)identifier @@ -77,14 +91,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[identifier]; } -- (void)setChildren:(NSArray *)children +- (id)child { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; - for (id child in children) { - [finalChildren addObject:[child finalLayoutable]]; - } - self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; + return self.layoutChildren[kDefaultChildKey]; } - (NSArray *)children @@ -92,4 +101,35 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } +static Class gLayoutOptionsClass = [ASLayoutOptions class]; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass +{ + gLayoutOptionsClass = layoutOptionsClass; +} + ++ (ASLayoutOptions *)optionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; + [layoutOptions setValuesFromLayoutable:child]; + layoutOptions.isMutable = NO; + return layoutOptions; +} + ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child +{ + objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); + if (layoutOptions == nil) { + layoutOptions = [self optionsForChild:child]; + [self associateLayoutOptions:layoutOptions withChild:child]; + } + return objc_getAssociatedObject(child, @selector(setChild:)); +} + + + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ff1ee030a..8ba86fdc1b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -10,6 +10,7 @@ #import #import +#import @class ASLayout; @class ASLayoutSpec; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 07243f30cc..ef109d47ae 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -72,17 +72,6 @@ _spacing = spacing; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; - -#if DEBUG - for (id child in children) { - ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); @@ -91,9 +80,8 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { - NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index b23b4fbbdb..e26d0a0210 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -8,9 +8,10 @@ * */ -#import +#import +#import -@protocol ASStackLayoutable +@protocol ASStackLayoutable /** * @abstract Additional space to place before this object in the stacking direction. diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 38a119a79e..31fe00c1a7 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -31,17 +31,6 @@ return self; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; - -#if DEBUG - for (id child in children) { - ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStaticLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStaticLayoutable)]), @"child must conform to ASStaticLayoutable"); - } -#endif -} - - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { CGSize size = { @@ -50,16 +39,17 @@ }; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; - for (id child in self.children) { + for (id child in self.children) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; CGSize autoMaxSize = { - constrainedSize.max.width - child.position.x, - constrainedSize.max.height - child.position.y + constrainedSize.max.width - layoutOptions.position.x, + constrainedSize.max.height - layoutOptions.position.y }; - ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) + ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) - : ASRelativeSizeRangeResolve(child.sizeRange, size); + : ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size); ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; - sublayout.position = child.position; + sublayout.position = layoutOptions.position; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index 5618fa6e9b..8e7d74acf3 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -8,10 +8,9 @@ * */ -#import #import -@protocol ASStaticLayoutable +@protocol ASStaticLayoutable /** If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index 2e3d2b3bfe..bf0239c952 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -15,15 +15,14 @@ static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - - __weak id child = (id) layout.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return child.ascender; + return layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + child.descender; + return layout.size.height + layoutOptions.descender; } } @@ -34,10 +33,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id child = (id)l.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - child.ascender; + return maxAscender - layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -91,9 +90,11 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; + ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; + ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; + return layoutOptionsA.ascender < layoutOptionsB.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -106,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id child = (id) l.layoutableObject; - p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -124,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = child.descender; + spacingAfterBaseline = layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -151,12 +152,12 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi /* Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements - ASBaselineLayoutable and needs an ascender and descender to lay itself out properly. + ASLayoutable and needs an ascender and descender to lay itself out properly. */ const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id)(*descenderIt).layoutableObject).descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 84bbdac502..31ff00b595 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -19,7 +19,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - switch (alignment(l.child.alignSelf, style.alignItems)) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + switch (alignment(layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -48,14 +49,15 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - p = p + directionPoint(style.direction, l.child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 93a2efcb20..4112af8e66 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -16,7 +16,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout { const CGFloat violation; /** Given a set of children, computes the unpositioned layouts for those children. */ - static ASStackUnpositionedLayout compute(const std::vector> &children, + static ASStackUnpositionedLayout compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange); }; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index cc3be53d1c..da4000cd5e 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,14 +18,15 @@ /** Sizes the child given the parameters specified, and returns the computed layout. */ -static ASLayout *crossChildLayout(const id child, +static ASLayout *crossChildLayout(const id child, const ASStackLayoutSpecStyle style, const CGFloat stackMin, const CGFloat stackMax, const CGFloat crossMin, const CGFloat crossMax) { - const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.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); @@ -75,7 +76,8 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -111,7 +113,8 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - return child.flexGrow && child.flexShrink; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + return layoutOptions.flexGrow && layoutOptions.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. */ -ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, +ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { @@ -283,7 +287,7 @@ static void flexChildrenAlongStackDimension(std::vector Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and stretched. */ -static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, +static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange, const CGSize size, @@ -292,9 +296,10 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); - return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; @@ -312,7 +317,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac }); } -ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, +ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 5a7cbd0a92..6ea803ba93 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -94,7 +94,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - foregroundNode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 9b6c3d655d..2e9c2e7802 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -41,8 +41,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - subnode.flexGrow = flex; - subnode.flexShrink = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; } return subnodes; } @@ -114,7 +114,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - ((ASDisplayNode *)subnodes[1]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; // 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}}; @@ -204,23 +204,25 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; + layoutOptions1.spacingBefore = 10; + layoutOptions2.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0; + layoutOptions1.spacingBefore = 0; + layoutOptions2.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20; + layoutOptions1.spacingAfter = 10; + layoutOptions2.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0; + layoutOptions1.spacingAfter = 0; + layoutOptions2.spacingAfter = 0; style.spacing = 10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10; + layoutOptions1.spacingBefore = -10; + layoutOptions2.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -236,9 +238,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -254,9 +256,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - child1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - child1.flexGrow = YES; - child1.flexShrink = YES; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; + layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); + layoutOptions1.flexGrow = YES; + layoutOptions1.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -271,11 +274,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - subnode1.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -291,7 +294,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.alignSelf = ASStackLayoutAlignSelfCenter; + [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -311,9 +314,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -332,9 +335,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -353,9 +356,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -374,9 +377,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -396,9 +399,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -419,8 +422,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; + layoutOptions.flexGrow = YES; + layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -439,12 +443,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - ((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -460,7 +464,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20); + [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -478,8 +482,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - child2.flexGrow = YES; - child2.flexShrink = YES; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; + layoutOptions2.flexGrow = YES; + layoutOptions2.flexShrink = YES; // 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. @@ -504,13 +509,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - ((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - ((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - ((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].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. diff --git a/Podfile.lock b/Podfile.lock index 423c2d2851..119b17c410 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.37.2 +COCOAPODS: 0.35.0 diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 293b513d1e..1e08bbe8c1 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -58,7 +58,9 @@ 1A943BF0259746F18D6E423F /* Frameworks */, 1AE410B73DA5C3BD087ACDD7 /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 05E2128219D4DB510098F589 /* Products */ = { isa = PBXGroup; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index d1f49351a2..410e4a097a 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -135,7 +135,9 @@ static const CGFloat kInnerPadding = 10.0f; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); - _textNode.flexShrink = YES; + ASLayoutOptions *textNodeOptions = [[ASLayoutOptions alloc] init]; + textNodeOptions.flexShrink = YES; + [ASLayoutSpec associateLayoutOptions:textNodeOptions withChild:_textNode]; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; From 9a702a49ad5587bbb819aff5da43daee2ea414fc Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 14:30:50 -0700 Subject: [PATCH 04/12] Hide ASLayoutOptions from the user --- AsyncDisplayKit.xcodeproj/project.pbxproj | 26 +++- AsyncDisplayKit/ASDisplayNode.mm | 11 +- AsyncDisplayKit/Layout/ASLayoutOptions.h | 5 +- AsyncDisplayKit/Layout/ASLayoutOptions.m | 18 ++- .../Layout/ASLayoutOptionsPrivate.h | 30 ++++ .../Layout/ASLayoutOptionsPrivate.mm | 129 ++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.h | 6 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 23 ++-- AsyncDisplayKit/Layout/ASLayoutable.h | 29 ++-- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 10 +- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 2 +- .../Private/ASBaselinePositionedLayout.mm | 28 ++-- .../Private/ASDisplayNodeInternal.h | 5 +- AsyncDisplayKit/Private/ASLayoutablePrivate.h | 16 +++ .../Private/ASStackPositionedLayout.mm | 9 +- .../Private/ASStackUnpositionedLayout.mm | 22 ++- .../ASCenterLayoutSpecSnapshotTests.mm | 3 +- .../ASStackLayoutSpecSnapshotTests.mm | 100 +++++++------- examples/Kittens/Sample/KittenNode.mm | 4 +- 19 files changed, 345 insertions(+), 131 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm create mode 100644 AsyncDisplayKit/Private/ASLayoutablePrivate.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 505370e6e5..cc0c1c2600 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -229,12 +229,18 @@ 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -471,7 +477,7 @@ 058D09DD195D050800B7D73C /* ASImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageNode.h; sourceTree = ""; }; 058D09DE195D050800B7D73C /* ASImageNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASImageNode.mm; sourceTree = ""; }; 058D09DF195D050800B7D73C /* ASTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode.h; sourceTree = ""; }; - 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; }; 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayLayer.h; sourceTree = ""; }; 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayLayer.mm; sourceTree = ""; }; 058D09E4195D050800B7D73C /* _ASDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayView.h; sourceTree = ""; }; @@ -580,9 +586,12 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; - 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; + 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; + 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; + 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Private/ASLayoutablePrivate.h; sourceTree = ""; }; + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -984,6 +993,7 @@ ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */, ACF6ED111B17843500DA7C62 /* ASLayoutable.h */, + 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */, ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */, ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */, ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */, @@ -1001,6 +1011,8 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, + 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); name = Layout; path = ..; @@ -1077,6 +1089,7 @@ 058D0A4F195D05CB00B7D73C /* ASImageNode.h in Headers */, 058D0A50195D05CB00B7D73C /* ASImageNode.mm in Headers */, 058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */, + 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */, 296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */, 058D0A52195D05CB00B7D73C /* ASTextNode.mm in Headers */, 055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */, @@ -1118,6 +1131,7 @@ 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, 058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, + 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */, 058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */, 058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, @@ -1170,6 +1184,7 @@ B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */, 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, + 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */, B31A241E1B0114FD0016AE7A /* AsyncDisplayKit.h in Headers */, B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */, B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */, @@ -1257,6 +1272,7 @@ B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, + 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1462,6 +1478,7 @@ 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, @@ -1573,6 +1590,7 @@ B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */, B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */, 34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */, + 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, 34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */, B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */, 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 291bd16285..3c02472fc6 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -9,6 +9,7 @@ #import "ASDisplayNode.h" #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNodeInternal.h" +#import "ASLayoutOptionsPrivate.h" #import @@ -41,6 +42,7 @@ @implementation ASDisplayNode +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) @@ -652,11 +654,6 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible return NO; } -- (id)finalLayoutable -{ - return self; -} - /** * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined @@ -1834,6 +1831,10 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; } +- (id)finalLayoutable { + return self; +} + @end @implementation ASDisplayNode (Debugging) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 0bdee1ad09..091f078ae9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -16,6 +16,9 @@ @interface ASLayoutOptions : NSObject ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; ++ (Class)defaultLayoutOptionsClass; + - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; @@ -42,7 +45,7 @@ #pragma mark - ASStaticLayoutable @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint position; +@property (nonatomic, readwrite) CGPoint layoutPosition; - (void)setupDefaults; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index 6207ef0d07..df3fc0fb61 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -15,6 +15,18 @@ @implementation ASLayoutOptions +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + return gDefaultLayoutOptionsClass; +} + + - (instancetype)initWithLayoutable:(id)layoutable; { self = [super init]; @@ -79,7 +91,7 @@ copy.descender = self.descender; copy.sizeRange = self.sizeRange; - copy.position = self.position; + copy.layoutPosition = self.layoutPosition; return copy; } @@ -98,7 +110,7 @@ _descender = 0; _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _position = CGPointZero; + _layoutPosition = CGPointZero; } // Do this here instead of in Node/Spec subclasses so that custom specs can set default values @@ -112,7 +124,7 @@ if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.position = displayNode.frame.origin; + self.layoutPosition = displayNode.frame.origin; } } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h new file mode 100644 index 0000000000..f7b7f9b9fc --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -0,0 +1,30 @@ +// +// ASDisplayNode+Layoutable.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import +#import + + +@interface ASDisplayNode() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASDisplayNode(ASLayoutOptions) +@end + +@interface ASLayoutSpec() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASLayoutSpec(ASLayoutOptions) +@end + diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm new file mode 100644 index 0000000000..b0332a8007 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -0,0 +1,129 @@ +// +// ASDisplayNode+Layoutable.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptionsPrivate.h" +#import + + +#define ASLayoutOptionsForwarding \ +- (ASLayoutOptions *)layoutOptions\ +{\ +if (_layoutOptions == nil) {\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ +return _layoutOptions;\ +}\ +\ +- (CGFloat)spacingBefore\ +{\ +return self.layoutOptions.spacingBefore;\ +}\ +\ +- (void)setSpacingBefore:(CGFloat)spacingBefore\ +{\ +self.layoutOptions.spacingBefore = spacingBefore;\ +}\ +\ +- (CGFloat)spacingAfter\ +{\ +return self.layoutOptions.spacingAfter;\ +}\ +\ +- (void)setSpacingAfter:(CGFloat)spacingAfter\ +{\ +self.layoutOptions.spacingAfter = spacingAfter;\ +}\ +\ +- (BOOL)flexGrow\ +{\ +return self.layoutOptions.flexGrow;\ +}\ +\ +- (void)setFlexGrow:(BOOL)flexGrow\ +{\ +self.layoutOptions.flexGrow = flexGrow;\ +}\ +\ +- (BOOL)flexShrink\ +{\ +return self.layoutOptions.flexShrink;\ +}\ +\ +- (void)setFlexShrink:(BOOL)flexShrink\ +{\ +self.layoutOptions.flexShrink = flexShrink;\ +}\ +\ +- (ASRelativeDimension)flexBasis\ +{\ +return self.layoutOptions.flexBasis;\ +}\ +\ +- (void)setFlexBasis:(ASRelativeDimension)flexBasis\ +{\ +self.layoutOptions.flexBasis = flexBasis;\ +}\ +\ +- (ASStackLayoutAlignSelf)alignSelf\ +{\ +return self.layoutOptions.alignSelf;\ +}\ +\ +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\ +{\ + self.layoutOptions.alignSelf = alignSelf;\ +}\ +\ +- (CGFloat)ascender\ +{\ + return self.layoutOptions.ascender;\ +}\ +\ +- (void)setAscender:(CGFloat)ascender\ +{\ + self.layoutOptions.ascender = ascender;\ +}\ +\ +- (CGFloat)descender\ +{\ + return self.layoutOptions.descender;\ +}\ +\ +- (void)setDescender:(CGFloat)descender\ +{\ + self.layoutOptions.descender = descender;\ +}\ +\ +- (ASRelativeSizeRange)sizeRange\ +{\ + return self.layoutOptions.sizeRange;\ +}\ +\ +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\ +{\ + self.layoutOptions.sizeRange = sizeRange;\ +}\ +\ +- (CGPoint)layoutPosition\ +{\ + return self.layoutOptions.layoutPosition;\ +}\ +\ +- (void)setLayoutPosition:(CGPoint)position\ +{\ + self.layoutOptions.layoutPosition = position;\ +}\ + + +@implementation ASDisplayNode(ASLayoutOptions) +ASLayoutOptionsForwarding +@end + +@implementation ASLayoutSpec(ASLayoutOptions) +ASLayoutOptionsForwarding +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index dc4f100bb5..494d5d1198 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,8 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; +//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; +//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; +//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index f277a01bc4..994ad8428e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -15,6 +15,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import "ASLayoutOptions.h" +#import "ASLayoutOptionsPrivate.h" #import @@ -27,6 +29,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -71,16 +74,20 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + if ([child respondsToSelector:@selector(finalLayoutable)]) { + id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; + } + } else { + [finalChildren addObject:child]; } - [finalChildren addObject:finalLayoutable]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 8ba86fdc1b..2ea76f3983 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -9,8 +9,10 @@ */ #import +#import #import -#import + +#import @class ASLayout; @class ASLayoutSpec; @@ -20,7 +22,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -31,16 +33,17 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -/** - @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before - being added to a ASLayoutSpec. - - For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement - layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method - a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. - - It is rare that a class will need to implement this method. - */ -- (id)finalLayoutable; +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 31fe00c1a7..8e97c9b42d 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -11,6 +11,8 @@ #import "ASStaticLayoutSpec.h" #import "ASLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" +#import "ASLayoutOptionsPrivate.h" #import "ASInternalHelpers.h" #import "ASLayout.h" #import "ASStaticLayoutable.h" @@ -40,16 +42,16 @@ NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; for (id child in self.children) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + ASLayoutOptions *layoutOptions = child.layoutOptions; CGSize autoMaxSize = { - constrainedSize.max.width - layoutOptions.position.x, - constrainedSize.max.height - layoutOptions.position.y + constrainedSize.max.width - layoutOptions.layoutPosition.x, + constrainedSize.max.height - layoutOptions.layoutPosition.y }; ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) : ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size); ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; - sublayout.position = layoutOptions.position; + sublayout.position = layoutOptions.layoutPosition; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index 8e7d74acf3..f2e565a7a9 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -18,6 +18,6 @@ @property (nonatomic, assign) ASRelativeSizeRange sizeRange; /** The position of this object within its parent spec. */ -@property (nonatomic, assign) CGPoint position; +@property (nonatomic, assign) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index bf0239c952..bf01ff179e 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -12,17 +12,19 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; + + __weak id child = layout.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return layoutOptions.ascender; + return child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + layoutOptions.descender; + return layout.size.height + child.layoutOptions.descender; } } @@ -33,10 +35,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + __weak id child = l.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - layoutOptions.ascender; + return maxAscender - child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -90,11 +92,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; - ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; - return layoutOptionsA.ascender < layoutOptionsB.ascender; + return a.layoutableObject.layoutOptions.ascender < b.layoutableObject.layoutOptions.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.layoutOptions.ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -107,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; - p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); + __weak id child = l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -125,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = layoutOptions.descender; + spacingAfterBaseline = child.layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -157,7 +157,7 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.layoutOptions.descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 5a8883ed02..d727bc5bab 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -17,6 +17,7 @@ #import "ASDisplayNode.h" #import "ASSentinel.h" #import "ASThread.h" +#import "ASLayoutOptions.h" BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); @@ -71,7 +72,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { NSMutableSet *_pendingDisplayNodes; _ASPendingState *_pendingViewState; - + struct { // public properties unsigned synchronous:1; @@ -153,6 +154,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; +- (id)finalLayoutable; + @end @interface UIView (ASDisplayNodeInternal) diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h new file mode 100644 index 0000000000..360c91570b --- /dev/null +++ b/AsyncDisplayKit/Private/ASLayoutablePrivate.h @@ -0,0 +1,16 @@ +// +// ASLayoutablePrivate.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 31ff00b595..95b445043c 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -14,13 +14,13 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" +#import "ASLayoutOptions.h" static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - switch (alignment(layoutOptions.alignSelf, style.alignItems)) { + switch (alignment(l.child.layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -49,15 +49,14 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, l.child.layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index da4000cd5e..7514507a01 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -14,6 +14,7 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" /** Sizes the child given the parameters specified, and returns the computed layout. @@ -25,8 +26,7 @@ static ASLayout *crossChildLayout(const id child, const CGFloat crossMin, const CGFloat crossMax) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(child.layoutOptions.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); @@ -76,8 +76,7 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(l.child.layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -113,8 +112,7 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexShrink; }; } } ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - return layoutOptions.flexGrow && layoutOptions.flexShrink; + return child.layoutOptions.flexGrow && child.layoutOptions.flexShrink; } /** @@ -297,9 +294,8 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 6ea803ba93..45e167afd7 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -13,6 +13,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASCenterLayoutSpec.h" #import "ASStackLayoutSpec.h" +#import "ASLayoutOptions.h" static const ASSizeRange kSize = {{100, 120}, {320, 160}}; @@ -94,7 +95,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; + foregroundNode.layoutOptions.flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 2e9c2e7802..fbd893940b 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -15,6 +15,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASRatioLayoutSpec.h" #import "ASInsetLayoutSpec.h" +#import "ASLayoutOptions.h" @interface ASStackLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase @end @@ -41,8 +42,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; - [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; + subnode.layoutOptions.flexGrow = flex; + subnode.layoutOptions.flexShrink = flex; } return subnodes; } @@ -114,7 +115,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; + ((ASDisplayNode *)subnodes[1]).layoutOptions.flexShrink = YES; // 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}}; @@ -204,25 +205,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; - layoutOptions1.spacingBefore = 10; - layoutOptions2.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - layoutOptions1.spacingBefore = 0; - layoutOptions2.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 0; - layoutOptions1.spacingAfter = 10; - layoutOptions2.spacingAfter = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - layoutOptions1.spacingAfter = 0; - layoutOptions2.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 0; style.spacing = 10; - layoutOptions1.spacingBefore = -10; - layoutOptions2.spacingAfter = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -238,9 +237,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -256,10 +255,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; - layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - layoutOptions1.flexGrow = YES; - layoutOptions1.flexShrink = YES; + child1.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(1); + child1.layoutOptions.flexGrow = YES; + child1.layoutOptions.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -274,11 +272,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; + subnode1.layoutOptions.flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; + subnode2.layoutOptions.flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -294,7 +292,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; + subnode2.layoutOptions.alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -314,9 +312,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -335,9 +333,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -356,9 +354,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -377,9 +375,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -399,9 +397,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -422,9 +420,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; - layoutOptions.flexGrow = YES; - layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); + subnode.layoutOptions.flexGrow = YES; + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -443,12 +440,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; + subnode.layoutOptions.flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -464,7 +461,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -482,9 +479,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; - layoutOptions2.flexGrow = YES; - layoutOptions2.flexShrink = YES; + child2.layoutOptions.flexGrow = YES; + child2.layoutOptions.flexShrink = YES; // 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. @@ -509,13 +505,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.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. diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 410e4a097a..d1f49351a2 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -135,9 +135,7 @@ static const CGFloat kInnerPadding = 10.0f; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); - ASLayoutOptions *textNodeOptions = [[ASLayoutOptions alloc] init]; - textNodeOptions.flexShrink = YES; - [ASLayoutSpec associateLayoutOptions:textNodeOptions withChild:_textNode]; + _textNode.flexShrink = YES; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; From e6a07ffe586d7431c83750db32e7dd59b5ff3a14 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 15:55:41 -0700 Subject: [PATCH 05/12] Kittens sample working --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 +++++------ AsyncDisplayKit/Layout/ASLayoutOptions.h | 16 +++++++------- AsyncDisplayKit/Layout/ASLayoutOptions.m | 21 ++++++++++++------- .../Layout/ASLayoutOptionsPrivate.h | 18 +++++++++------- .../Layout/ASLayoutOptionsPrivate.mm | 20 ++++++++++-------- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 ---- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 19 +++++++---------- AsyncDisplayKit/Layout/ASLayoutable.h | 18 ++++------------ AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 19 +++++++++++++++++ AsyncDisplayKit/Private/ASLayoutablePrivate.h | 16 -------------- 10 files changed, 82 insertions(+), 81 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutablePrivate.h delete mode 100644 AsyncDisplayKit/Private/ASLayoutablePrivate.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index cc0c1c2600..07f1563cf4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -237,10 +237,10 @@ 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; - 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -590,8 +590,8 @@ 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; - 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Private/ASLayoutablePrivate.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; + 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -993,7 +993,7 @@ ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */, ACF6ED111B17843500DA7C62 /* ASLayoutable.h */, - 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */, + 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */, ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */, ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */, ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */, @@ -1131,7 +1131,7 @@ 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, 058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, - 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */, + 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, 058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */, 058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, @@ -1272,7 +1272,7 @@ B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, - 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */, + 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 091f078ae9..3df0f7c673 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index df3fc0fb61..a383d8497e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptions.h" @@ -23,6 +25,11 @@ static Class gDefaultLayoutOptionsClass = nil; + (Class)defaultLayoutOptionsClass { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + }); return gDefaultLayoutOptionsClass; } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h index f7b7f9b9fc..b00e09580a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import #import @@ -13,6 +15,7 @@ @interface ASDisplayNode() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end @@ -22,6 +25,7 @@ @interface ASLayoutSpec() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index b0332a8007..431565bde1 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptionsPrivate.h" #import @@ -13,9 +15,9 @@ #define ASLayoutOptionsForwarding \ - (ASLayoutOptions *)layoutOptions\ {\ -if (_layoutOptions == nil) {\ +dispatch_once(&_layoutOptionsInitializeToken, ^{\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ -}\ +});\ return _layoutOptions;\ }\ \ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 494d5d1198..f132d5d7f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,4 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 994ad8428e..39487fe1ec 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -74,20 +74,17 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - if ([child respondsToSelector:@selector(finalLayoutable)]) { - id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; } else { [finalChildren addObject:child]; } - } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 2ea76f3983..38644c0b60 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -11,6 +11,9 @@ #import #import #import +#import +#import +#import #import @@ -22,7 +25,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -33,17 +36,4 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -@property (nonatomic, readwrite) CGFloat spacingBefore; -@property (nonatomic, readwrite) CGFloat spacingAfter; -@property (nonatomic, readwrite) BOOL flexGrow; -@property (nonatomic, readwrite) BOOL flexShrink; -@property (nonatomic, readwrite) ASRelativeDimension flexBasis; -@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; - -@property (nonatomic, readwrite) CGFloat ascender; -@property (nonatomic, readwrite) CGFloat descender; - -@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint layoutPosition; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h new file mode 100644 index 0000000000..5091a30954 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h deleted file mode 100644 index 360c91570b..0000000000 --- a/AsyncDisplayKit/Private/ASLayoutablePrivate.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASLayoutablePrivate.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// -#import - -@class ASLayoutSpec; -@class ASLayoutOptions; - -@protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; -@end From 2155054c69bd4872cbc814f05920fe26b8838818 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:49:40 -0700 Subject: [PATCH 06/12] wasn't copying layout options to final layoutable. --- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 39487fe1ec..a1ba8ed80a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -59,12 +59,25 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } +- (id)layoutableToAddFromLayoutable:(id)child +{ + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + return finalLayoutable; + } + return child; +} + - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - layoutOptions.isMutable = NO; - self.layoutChildren[identifier] = child; + self.layoutChildren[identifier] = [self layoutableToAddFromLayoutable:child];; } - (void)setChildren:(NSArray *)children @@ -73,18 +86,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } else { - [finalChildren addObject:child]; - } + [finalChildren addObject:[self layoutableToAddFromLayoutable:child]]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; From c1fef24c8adb6c836fb2f587cca9578bba2113f3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:51:15 -0700 Subject: [PATCH 07/12] bug in setting the ASLayoutOptions default class. --- AsyncDisplayKit/Layout/ASLayoutOptions.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index a383d8497e..e0ebf622f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -27,8 +27,10 @@ static Class gDefaultLayoutOptionsClass = nil; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } }); return gDefaultLayoutOptionsClass; } From f6d67689854488f158fe91c007c08d7c9ca19ac1 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:11:19 -0700 Subject: [PATCH 08/12] Fixed infinite recursion in finalLayoutable, removed child properties from ASLayoutSpecs --- AsyncDisplayKit/ASDisplayNode.mm | 5 ++- .../Layout/ASBackgroundLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASCenterLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASInsetLayoutSpec.h | 1 - .../Layout/ASLayoutOptionsPrivate.mm | 7 ++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 42 +++---------------- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 4 +- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 2 +- AsyncDisplayKit/Layout/ASRatioLayoutSpec.h | 2 - .../Private/ASDisplayNodeInternal.h | 2 - 11 files changed, 19 insertions(+), 49 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 3c02472fc6..d202ceedf0 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1831,8 +1831,9 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; } -- (id)finalLayoutable { - return self; +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec +{ + return nil; } @end diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h index ecc2e48473..cab51ea8b3 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASBackgroundLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id background; /** diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index dc50b265e7..37ba24e7e7 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -39,7 +39,6 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { @property (nonatomic, assign) ASCenterLayoutSpecCenteringOptions centeringOptions; @property (nonatomic, assign) ASCenterLayoutSpecSizingOptions sizingOptions; -@property (nonatomic, strong) id child; /** * Initializer. diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h index 3b140dfcc7..ab0a6f106f 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h @@ -29,7 +29,6 @@ */ @interface ASInsetLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, assign) UIEdgeInsets insets; /** diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index 431565bde1..f04c010936 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -16,11 +16,18 @@ - (ASLayoutOptions *)layoutOptions\ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ +if (_layoutOptions == nil) {\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ });\ return _layoutOptions;\ }\ \ +- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ +{\ + _layoutOptions = layoutOptions;\ +}\ +\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index a1ba8ed80a..904c30cc69 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -49,9 +49,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } -- (id)finalLayoutable +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; { - return self; + return nil; } - (void)setChild:(id)child; @@ -61,14 +61,14 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [child layoutOptions]; layoutOptions.isMutable = NO; - if (finalLayoutable != child) { + id finalLayoutable = [child finalLayoutableWithParent:self]; + if (finalLayoutable) { ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + finalLayoutable.layoutOptions = finalLayoutOptions; return finalLayoutable; } return child; @@ -107,35 +107,5 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } -static Class gLayoutOptionsClass = [ASLayoutOptions class]; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass -{ - gLayoutOptionsClass = layoutOptionsClass; -} - -+ (ASLayoutOptions *)optionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; - [layoutOptions setValuesFromLayoutable:child]; - layoutOptions.isMutable = NO; - return layoutOptions; -} - -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child -{ - objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); - if (layoutOptions == nil) { - layoutOptions = [self optionsForChild:child]; - [self associateLayoutOptions:layoutOptions withChild:child]; - } - return objc_getAssociatedObject(child, @selector(setChild:)); -} - - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 5091a30954..c7ab3b7ad3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -14,6 +14,6 @@ @class ASLayoutOptions; @protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; +@property (nonatomic, strong) ASLayoutOptions *layoutOptions; @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h index 35a9577dde..05e53d92e8 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASOverlayLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id overlay; + (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay; diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 05c1c0c4ca..b71375f5be 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -49,7 +49,7 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; if (self.overlay) { diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h index fd7f6d657b..2affa56a75 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h @@ -31,8 +31,6 @@ **/ @interface ASRatioLayoutSpec : ASLayoutSpec - -@property (nonatomic, strong) id child; @property (nonatomic, assign) CGFloat ratio; + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index d727bc5bab..cff5d76461 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -154,8 +154,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; -- (id)finalLayoutable; - @end @interface UIView (ASDisplayNodeInternal) From b84cca0d4ea4582b30b5bb29c4b7cad4b96f35b4 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:14:09 -0700 Subject: [PATCH 09/12] didn't mean to commit this --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 119b17c410..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.35.0 +COCOAPODS: 0.37.2 From b07078fed62c06c595ece807aceba3c73c62bb16 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:41:40 -0700 Subject: [PATCH 10/12] Make layoutOptions readonly --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 9 +++- AsyncDisplayKit/Layout/ASLayoutOptions.m | 44 +++++++++++-------- .../Layout/ASLayoutOptionsPrivate.mm | 7 +-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 +-- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 2 +- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 3df0f7c673..937f6c7640 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -24,6 +24,14 @@ - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; +#pragma mark - Subclasses should implement these! ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor; +- (void)setupDefaults; +- (instancetype)copyWithZone:(NSZone *)zone; +- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; + +#pragma mark - Mutability checks + @property (nonatomic, assign) BOOL isMutable; #if DEBUG @@ -49,6 +57,5 @@ @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; @property (nonatomic, readwrite) CGPoint layoutPosition; -- (void)setupDefaults; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index e0ebf622f5..9d8d9c6a0e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -13,7 +13,6 @@ #import #import #import "ASInternalHelpers.h" -#import @implementation ASLayoutOptions @@ -35,6 +34,10 @@ static Class gDefaultLayoutOptionsClass = nil; return gDefaultLayoutOptionsClass; } +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} - (instancetype)initWithLayoutable:(id)layoutable; { @@ -42,30 +45,31 @@ static Class gDefaultLayoutOptionsClass = nil; if (self) { [self setupDefaults]; [self setValuesFromLayoutable:layoutable]; + _isMutable = YES; #if DEBUG - [self addObserver:self forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; + [self addObserver:self + forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; #endif } return self; } +- (void)dealloc +{ +#if DEBUG + [self removeObserver:self forKeyPath:@"changeMonitor"]; +#endif +} + #if DEBUG + (NSSet *)keyPathsForValuesAffectingChangeMonitor { NSMutableSet *keys = [NSMutableSet set]; - unsigned int count; - - objc_property_t *properties = class_copyPropertyList([self class], &count); - for (size_t i = 0; i < count; ++i) { - NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; - - if ([property isEqualToString: @"observableSelf"] == NO) { - [keys addObject: property]; - } - } - free(properties); + [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; + [keys addObjectsFromArray:@[@"ascender", @"descender"]]; + [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; return keys; } @@ -89,7 +93,12 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ copy.flexBasis = self.flexBasis; copy.spacingAfter = self.spacingAfter; copy.spacingBefore = self.spacingBefore; @@ -98,13 +107,12 @@ static Class gDefaultLayoutOptionsClass = nil; copy.ascender = self.ascender; copy.descender = self.descender; - + copy.sizeRange = self.sizeRange; copy.layoutPosition = self.layoutPosition; - - return copy; } + #pragma mark - Defaults - (void)setupDefaults { diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index f04c010936..39d129ba11 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -17,17 +17,12 @@ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ if (_layoutOptions == nil) {\ -_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] initWithLayoutable:self];\ }\ });\ return _layoutOptions;\ }\ \ -- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ -{\ - _layoutOptions = layoutOptions;\ -}\ -\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 904c30cc69..6c506b655c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -66,9 +66,8 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - finalLayoutable.layoutOptions = finalLayoutOptions; + [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; + finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index c7ab3b7ad3..2d317fc7ca 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -15,5 +15,5 @@ @protocol ASLayoutablePrivate - (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; -@property (nonatomic, strong) ASLayoutOptions *layoutOptions; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; @end From bca7d838e19187fd5412eecd24db1c09dcb54c05 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 31 Aug 2015 14:19:27 -0700 Subject: [PATCH 11/12] making ASLayoutOptions threadsafe --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 10 - AsyncDisplayKit/Layout/ASLayoutOptions.m | 149 ------------- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 250 ++++++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 - 4 files changed, 250 insertions(+), 161 deletions(-) delete mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.m create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.mm diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 937f6c7640..022bbb61f3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -25,19 +25,9 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor; - (void)setupDefaults; -- (instancetype)copyWithZone:(NSZone *)zone; - (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; -#pragma mark - Mutability checks - -@property (nonatomic, assign) BOOL isMutable; - -#if DEBUG -@property (nonatomic, assign) NSUInteger changeMonitor; -#endif - #pragma mark - ASStackLayoutable @property (nonatomic, readwrite) CGFloat spacingBefore; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m deleted file mode 100644 index 9d8d9c6a0e..0000000000 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import "ASLayoutOptions.h" - -#import -#import -#import "ASInternalHelpers.h" - -@implementation ASLayoutOptions - -static Class gDefaultLayoutOptionsClass = nil; -+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass -{ - gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; -} - -+ (Class)defaultLayoutOptionsClass -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (gDefaultLayoutOptionsClass == nil) { - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; - } - }); - return gDefaultLayoutOptionsClass; -} - -- (instancetype)init -{ - return [self initWithLayoutable:nil]; -} - -- (instancetype)initWithLayoutable:(id)layoutable; -{ - self = [super init]; - if (self) { - [self setupDefaults]; - [self setValuesFromLayoutable:layoutable]; - _isMutable = YES; -#if DEBUG - [self addObserver:self - forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; -#endif - } - return self; -} - -- (void)dealloc -{ -#if DEBUG - [self removeObserver:self forKeyPath:@"changeMonitor"]; -#endif -} - -#if DEBUG -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor -{ - NSMutableSet *keys = [NSMutableSet set]; - [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; - [keys addObjectsFromArray:@[@"ascender", @"descender"]]; - [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; - - return keys; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ -#if DEBUG - if ([keyPath isEqualToString:@"changeMonitor"]) { - ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); - } else -#endif - { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - - -#pragma mark - NSCopying -- (id)copyWithZone:(NSZone *)zone -{ - ASLayoutOptions *copy = [[[self class] alloc] init]; - [self copyIntoOptions:copy]; - return copy; -} - -- (void)copyIntoOptions:(ASLayoutOptions *)copy -{ - copy.flexBasis = self.flexBasis; - copy.spacingAfter = self.spacingAfter; - copy.spacingBefore = self.spacingBefore; - copy.flexGrow = self.flexGrow; - copy.flexShrink = self.flexShrink; - - copy.ascender = self.ascender; - copy.descender = self.descender; - - copy.sizeRange = self.sizeRange; - copy.layoutPosition = self.layoutPosition; -} - - -#pragma mark - Defaults -- (void)setupDefaults -{ - _flexBasis = ASRelativeDimensionUnconstrained; - _spacingBefore = 0; - _spacingAfter = 0; - _flexGrow = NO; - _flexShrink = NO; - _alignSelf = ASStackLayoutAlignSelfAuto; - - _ascender = 0; - _descender = 0; - - _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _layoutPosition = CGPointZero; -} - -// Do this here instead of in Node/Spec subclasses so that custom specs can set default values -- (void)setValuesFromLayoutable:(id)layoutable -{ - if ([layoutable isKindOfClass:[ASTextNode class]]) { - ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); - } - if ([layoutable isKindOfClass:[ASDisplayNode class]]) { - ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; - self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.layoutPosition = displayNode.frame.origin; - } -} - - -@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm new file mode 100644 index 0000000000..ad6d224426 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASLayoutOptions.h" + +#import +#import +#import +#import "ASInternalHelpers.h" + +@interface ASLayoutOptions() +{ + ASDN::RecursiveMutex _propertyLock; +} +@end + +@implementation ASLayoutOptions + +@synthesize spacingBefore = _spacingBefore; +@synthesize spacingAfter = _spacingAfter; +@synthesize flexGrow = _flexGrow; +@synthesize flexShrink = _flexShrink; +@synthesize flexBasis = _flexBasis; +@synthesize alignSelf = _alignSelf; + +@synthesize ascender = _ascender; +@synthesize descender = _descender; + +@synthesize sizeRange = _sizeRange; +@synthesize layoutPosition = _layoutPosition; + +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } + }); + return gDefaultLayoutOptionsClass; +} + +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; + } + return self; +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} + +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ + ASDN::MutexLocker l(_propertyLock); + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.layoutPosition = self.layoutPosition; +} + + +#pragma mark - Defaults +- (void)setupDefaults +{ + self.flexBasis = ASRelativeDimensionUnconstrained; + self.spacingBefore = 0; + self.spacingAfter = 0; + self.flexGrow = NO; + self.flexShrink = NO; + self.alignSelf = ASStackLayoutAlignSelfAuto; + + self.ascender = 0; + self.descender = 0; + + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + self.layoutPosition = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + ASDN::MutexLocker l(_propertyLock); + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.layoutPosition = displayNode.frame.origin; + } +} + +- (CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingAfter; +} + +- (void)setSpacingAfter:(CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + _spacingAfter = spacingAfter; +} + +- (CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingBefore; +} + +- (void)setSpacingBefore:(CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + _spacingBefore = spacingBefore; +} + +- (BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + return _flexGrow; +} + +- (void)setFlexGrow:(BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + _flexGrow = flexGrow; +} + +- (BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + return _flexShrink; +} + +- (void)setFlexShrink:(BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + _flexShrink = flexShrink; +} + +- (ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + return _flexBasis; +} + +- (void)setFlexBasis:(ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + _flexBasis = flexBasis; +} + +- (ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + return _alignSelf; +} + +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + _alignSelf = alignSelf; +} + +- (CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + return _ascender; +} + +- (void)setAscender:(CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + _ascender = ascender; +} + +- (CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + return _descender; +} + +- (void)setDescender:(CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + _descender = descender; +} + +- (ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + return _sizeRange; +} + +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + _sizeRange = sizeRange; +} + +- (CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + return _layoutPosition; +} + +- (void)setLayoutPosition:(CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + _layoutPosition = layoutPosition; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 6c506b655c..0985014b80 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -62,12 +62,10 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { ASLayoutOptions *layoutOptions = [child layoutOptions]; - layoutOptions.isMutable = NO; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; - finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; From ad95a7c3eaee96e91c46d91da30d7d70da2d0fa3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 8 Sep 2015 09:48:59 -0700 Subject: [PATCH 12/12] add locks to ASLayoutOptions --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++------ AsyncDisplayKit/Layout/ASLayoutOptions.mm | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 07f1563cf4..bc841c2034 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -231,8 +231,8 @@ 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; - 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; @@ -587,7 +587,7 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; - 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptions.mm; path = AsyncDisplayKit/Layout/ASLayoutOptions.mm; sourceTree = ""; }; 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; @@ -1010,7 +1010,7 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, - 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); @@ -1477,7 +1477,7 @@ ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, - 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, @@ -1606,7 +1606,7 @@ B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, - 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */, diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index ad6d224426..9e499bd172 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -117,8 +117,10 @@ static Class gDefaultLayoutOptionsClass = nil; ASDN::MutexLocker l(_propertyLock); if ([layoutable isKindOfClass:[ASTextNode class]]) { ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + if (textNode.attributedString.length > 0) { + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } } if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable;