From 176e4962bfad1324328230f53b0d16f2273b41f2 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Fri, 14 Aug 2015 14:41:13 -0700 Subject: [PATCH 01/54] Perform one-time initializations in +initialize when possible --- AsyncDisplayKit/ASDisplayNode.mm | 135 ++++++++++++------ .../Private/ASDisplayNodeInternal.h | 2 +- AsyncDisplayKit/Private/ASInternalHelpers.h | 1 + AsyncDisplayKit/Private/ASInternalHelpers.mm | 9 ++ 4 files changed, 102 insertions(+), 45 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 02cf7b31..32d284f5 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -30,6 +30,8 @@ * */ +- (void)_staticInitialize; + @end // Conditionally time these scopes to our debug ivars (only exist in debug/profile builds) @@ -85,25 +87,93 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } } -+ (void)initialize -{ - if (self == [ASDisplayNode class]) { - return; +/** + * Returns ASDisplayNodeFlags for the givern class/instance. instance MAY BE NIL. + * + * @param c the class, required + * @param instance the instance, which may be nil. (If so, the class is inspected instead) + * + * @return ASDisplayNode flags. + */ +static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance) { + struct ASDisplayNodeFlags flags = {0}; + + flags.isInHierarchy = NO; + flags.displaysAsynchronously = YES; + flags.implementsDrawRect = ([c respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); + flags.implementsImageDisplay = ([c respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); + if (instance) { + flags.implementsDrawParameters = ([instance respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + } else { + flags.implementsDrawParameters = ([c instancesRespondToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); + } + return flags; +} + +/** + * Returns ASDisplayNodeMethodOverrides for the given class + * + * @param c the class, requireed. + * + * @return ASDisplayNodeMethodOverrides. + */ +static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { + ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone; + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesBegan; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesMoved:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesMoved; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesCancelled:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesCancelled; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesEnded:withEvent:))) { + overrides |= ASDisplayNodeMethodOverrideTouchesEnded; + } + if (ASDisplayNodeSubclassOverridesSelector(c, @selector(calculateSizeThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits; } - // Subclasses should never override these - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self)); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self)); + return overrides; +} - // At most one of the three layout methods is overridden - ASDisplayNodeAssert((ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateSizeThatFits:)) ? 1 : 0) - + (ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutSpecThatFits:)) ? 1 : 0) - + (ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1, - @"Subclass %@ must override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self)); ++ (void)initialize +{ + if (self != [ASDisplayNode class]) { + + // Subclasses should never override these + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self)); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self)); + + // At most one of the three layout methods is overridden + ASDisplayNodeAssert((ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateSizeThatFits:)) ? 1 : 0) + + (ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutSpecThatFits:)) ? 1 : 0) + + (ASDisplayNodeSubclassOverridesSelector(self, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1, + @"Subclass %@ must override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self)); + } + + // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values + // when each instance is constructed. These values don't change for each class, so there is significant performance benefit + // in doing it here. +initialize is guaranteed to be called before any instance method so it is safe to add this method here. + // Note that we take care to detect if the class overrides +respondsToSelector: or -respondsToSelector and take the slow path + // (recalculating for each instance) to make sure we are always correct. + + BOOL classOverridesRespondsToSelector = ASSubclassOverridesClassSelector([NSObject class], self, @selector(respondsToSelector:)); + BOOL instancesOverrideRespondsToSelector = ASSubclassOverridesSelector([NSObject class], self, @selector(respondsToSelector:)); + struct ASDisplayNodeFlags flags = GetASDisplayNodeFlags(self, nil); + ASDisplayNodeMethodOverrides methodOverrides = GetASDisplayNodeMethodOverrides(self); + + IMP staticInitialize = imp_implementationWithBlock(^(ASDisplayNode *node) { + node->_flags = (classOverridesRespondsToSelector || instancesOverrideRespondsToSelector) ? GetASDisplayNodeFlags(node.class, node) : flags; + node->_methodOverrides = (classOverridesRespondsToSelector) ? GetASDisplayNodeMethodOverrides(node.class) : methodOverrides; + }); + + class_replaceMethod(self, @selector(_staticInitialize), staticInitialize, "v:@"); } + (BOOL)layerBackedNodesEnabled @@ -123,38 +193,15 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) #pragma mark - Lifecycle +- (void)_staticInitialize { + ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden"); +} + - (void)_initializeInstance { + [self _staticInitialize]; _contentsScaleForDisplay = ASScreenScale(); - _displaySentinel = [[ASSentinel alloc] init]; - - _flags.isInHierarchy = NO; - _flags.displaysAsynchronously = YES; - - // As an optimization, it may be worth a caching system that performs these checks once per class in +initialize (see above). - _flags.implementsDrawRect = ([[self class] respondsToSelector:@selector(drawRect:withParameters:isCancelled:isRasterizing:)] ? 1 : 0); - _flags.implementsImageDisplay = ([[self class] respondsToSelector:@selector(displayWithParameters:isCancelled:)] ? 1 : 0); - _flags.implementsDrawParameters = ([self respondsToSelector:@selector(drawParametersForAsyncLayer:)] ? 1 : 0); - - ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone; - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesBegan:withEvent:))) { - overrides |= ASDisplayNodeMethodOverrideTouchesBegan; - } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesMoved:withEvent:))) { - overrides |= ASDisplayNodeMethodOverrideTouchesMoved; - } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesCancelled:withEvent:))) { - overrides |= ASDisplayNodeMethodOverrideTouchesCancelled; - } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesEnded:withEvent:))) { - overrides |= ASDisplayNodeMethodOverrideTouchesEnded; - } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(calculateSizeThatFits:))) { - overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits; - } - _methodOverrides = overrides; - _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index c3e476a4..309cd08f 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -72,7 +72,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { _ASPendingState *_pendingViewState; - struct { + struct ASDisplayNodeFlags { // public properties unsigned synchronous:1; unsigned layerBacked:1; diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 8693440e..b0530db7 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -15,6 +15,7 @@ ASDISPLAYNODE_EXTERN_C_BEGIN BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector); +BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector); CGFloat ASScreenScale(); diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index f230e1e4..ddfcaf5d 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -24,6 +24,15 @@ BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector) return (superclassIMP != subclassIMP); } +BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector) +{ + Method superclassMethod = class_getClassMethod(superclass, selector); + Method subclassMethod = class_getClassMethod(subclass, selector); + IMP superclassIMP = superclassMethod ? method_getImplementation(superclassMethod) : NULL; + IMP subclassIMP = subclassMethod ? method_getImplementation(subclassMethod) : NULL; + return (superclassIMP != subclassIMP); +} + static void ASDispatchOnceOnMainThread(dispatch_once_t *predicate, dispatch_block_t block) { if ([NSThread isMainThread]) { From 36bac93a53172b53eb2b59624a2a239faca4f436 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Thu, 20 Aug 2015 10:55:15 -0700 Subject: [PATCH 02/54] ensure class is not nil --- AsyncDisplayKit/ASDisplayNode.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 06b00c56..0b639f2a 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -96,6 +96,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) * @return ASDisplayNode flags. */ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance) { + ASDisplayNodeCAssertNotNil(c, @"class is required"); + struct ASDisplayNodeFlags flags = {0}; flags.isInHierarchy = NO; @@ -118,6 +120,8 @@ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *i * @return ASDisplayNodeMethodOverrides. */ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { + ASDisplayNodeCAssertNotNil(c, @"class is required"); + ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone; if (ASDisplayNodeSubclassOverridesSelector(c, @selector(touchesBegan:withEvent:))) { overrides |= ASDisplayNodeMethodOverrideTouchesBegan; From 641929c4e5cccc3a5cd92c33fab1a052bf6e6caf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 21 Aug 2015 17:22:55 +0300 Subject: [PATCH 03/54] By default, cell nodes in table view should fill its width. So the min constrained width is updated to enforce this behaviour. --- AsyncDisplayKit/ASTableView.mm | 3 ++- examples/Kittens/Sample/KittenNode.mm | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 6a9a3bd3..8948591d 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -791,7 +791,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } // Default size range - return ASSizeRangeMake(CGSizeZero, CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); + return ASSizeRangeMake(CGSizeMake(_maxWidthForNodesConstrainedSize, 0), + CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); } - (void)dataControllerLockDataSource diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index a852bcb8..84e4b49e 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -136,6 +136,7 @@ static const CGFloat kInnerPadding = 10.0f; { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); _textNode.flexShrink = YES; + _textNode.flexGrow = YES; return [ASInsetLayoutSpec From 499adb514566fd8917febf94461cb6d9803d4ea7 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:04:06 -0700 Subject: [PATCH 04/54] 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 0809fbf5..38e68769 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 ab1a6440..f06fb638 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 fd3cfce1..ea18aca9 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 8b784a56..46587d99 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 670c69c9..45fa687f 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 18308953..58dc55f9 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 5ca96ab8..cc6ec941 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 bb200cbf..f599c848 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 ef09aa36..58ecd114 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 4ac01ee0..4ff1ee03 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 f4f02557..05c1c0c4 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 5e5c6f0c..64e9e2c1 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 7825a9b8..b5a126c5 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 3149979d..07243f30 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 38ed2f06..8bd3f3d9 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 61a6d172..38a119a7 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 00000000..5618fa6e --- /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 05/54] 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 faf2cc96..d1f49351 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 98b41a4b1cb706195623596750275e27cd3183b4 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 26 Aug 2015 16:15:18 -0700 Subject: [PATCH 06/54] scrollable directions was invalid for non-flow layouts as bounds and contentSize were zero on init --- .../Details/ASCollectionViewLayoutController.mm | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index 86551027..cbed7f2b 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -13,6 +13,7 @@ #import "ASAssert.h" #import "ASCollectionView.h" #import "CGRect+ASConvenience.h" +#import "UICollectionViewLayout+ASConvenience.h" struct ASDirectionalScreenfulBuffer { CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space. @@ -56,7 +57,7 @@ typedef struct ASRangeGeometry ASRangeGeometry; @interface ASCollectionViewLayoutController () { - UIScrollView * __weak _scrollView; + ASCollectionView * __weak _collectionView; UICollectionViewLayout * __strong _collectionViewLayout; std::vector _updateRangeBoundsIndexedByRangeType; ASScrollDirection _scrollableDirections; @@ -72,7 +73,7 @@ typedef struct ASRangeGeometry ASRangeGeometry; } _scrollableDirections = [collectionView scrollableDirections]; - _scrollView = collectionView; + _collectionView = collectionView; _collectionViewLayout = [collectionView collectionViewLayout]; _updateRangeBoundsIndexedByRangeType = std::vector(ASLayoutRangeTypeCount); return self; @@ -94,8 +95,13 @@ typedef struct ASRangeGeometry ASRangeGeometry; - (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection rangeTuningParameters:(ASRangeTuningParameters)rangeTuningParameters { - CGRect rangeBounds = _scrollView.bounds; - CGRect updateBounds = _scrollView.bounds; + CGRect rangeBounds = _collectionView.bounds; + CGRect updateBounds = _collectionView.bounds; + + //scrollable directions can change for non-flow layouts + if ([_collectionViewLayout asdk_isFlowLayout] == NO) { + _scrollableDirections = [_collectionView scrollableDirections]; + } BOOL canScrollHorizontally = ASScrollDirectionContainsHorizontalDirection(_scrollableDirections); if (canScrollHorizontally) { @@ -148,7 +154,7 @@ typedef struct ASRangeGeometry ASRangeGeometry; return YES; } - CGRect currentBounds = _scrollView.bounds; + CGRect currentBounds = _collectionView.bounds; if (CGRectIsEmpty(currentBounds)) { currentBounds = CGRectMake(0, 0, viewportSize.width, viewportSize.height); } From 075c39e3981e68b655011e24cdd08c4994eaff1d Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 27 Aug 2015 20:57:53 +0300 Subject: [PATCH 07/54] When relayout a node, preserve its frame origin. --- AsyncDisplayKit/ASDisplayNode.mm | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index ab1a6440..59d0b783 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -600,9 +600,27 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } else { // This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used. [self measureWithSizeRange:oldConstrainedSize]; - CGRect bounds = self.bounds; - bounds.size = CGSizeMake(_layout.size.width, _layout.size.height); - self.bounds = bounds; + + CGSize oldSize = self.bounds.size; + CGSize newSize = _layout.size; + + if (! CGSizeEqualToSize(oldSize, newSize)) { + CGRect bounds = self.bounds; + bounds.size = newSize; + self.bounds = bounds; + + // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint + // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted. + BOOL useLayer = (_layer && ASDisplayNodeThreadIsMain()); + CGPoint anchorPoint = (useLayer ? _layer.anchorPoint : self.anchorPoint); + CGPoint oldPosition = (useLayer ? _layer.position : self.position); + + CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x; + CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y; + CGPoint newPosition = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta); + + useLayer ? _layer.position = newPosition : self.position = newPosition; + } } } From afade854af67ab6860cc4d2edfc78a9a5f8672f3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 09:36:22 -0700 Subject: [PATCH 08/54] 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 38e68769..505370e6 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 974bbe12..7fcfbd59 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 f06fb638..291bd162 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 1b35441c..e8d5b761 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 7ca68f51..68f4f1fd 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 46587d99..d147c767 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 45fa687f..50139a54 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 5e050299..7e52b2c0 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 00000000..0bdee1ad --- /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 00000000..6207ef0d --- /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 f599c848..dc4f100b 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 58ecd114..f277a01b 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 4ff1ee03..8ba86fdc 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 07243f30..ef109d47 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 b23b4fbb..e26d0a02 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 38a119a7..31fe00c1 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 5618fa6e..8e7d74ac 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 2e3d2b3b..bf0239c9 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 84bbdac5..31ff00b5 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 93a2efcb..4112af8e 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 cc3be53d..da4000cd 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 5a7cbd0a..6ea803ba 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 9b6c3d65..2e9c2e78 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 423c2d28..119b17c4 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 293b513d..1e08bbe8 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 d1f49351..410e4a09 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 62303ebeecbe1797f878dfcfaa3b74b79f77969f Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Fri, 28 Aug 2015 13:37:39 -0700 Subject: [PATCH 09/54] array must be init'd otherwise arrayByAddingObject returns nil. --- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 61a6d172..eca2637a 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -44,6 +44,11 @@ return [[self alloc] initWithChildren:children]; } +- (instancetype)init +{ + return [self initWithChildren:@[]]; +} + - (instancetype)initWithChildren:(NSArray *)children { if (!(self = [super init])) { From 9a702a49ad5587bbb819aff5da43daee2ea414fc Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 14:30:50 -0700 Subject: [PATCH 10/54] 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 505370e6..cc0c1c26 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 291bd162..3c02472f 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 0bdee1ad..091f078a 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 6207ef0d..df3fc0fb 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 00000000..f7b7f9b9 --- /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 00000000..b0332a80 --- /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 dc4f100b..494d5d11 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 f277a01b..994ad842 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 8ba86fdc..2ea76f39 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 31fe00c1..8e97c9b4 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 8e7d74ac..f2e565a7 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 bf0239c9..bf01ff17 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 5a8883ed..d727bc5b 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 00000000..360c9157 --- /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 31ff00b5..95b44504 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 da4000cd..7514507a 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 6ea803ba..45e167af 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 2e9c2e78..fbd89394 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 410e4a09..d1f49351 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 11/54] 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 cc0c1c26..07f1563c 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 091f078a..3df0f7c6 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 df3fc0fb..a383d849 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 f7b7f9b9..b00e0958 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 b0332a80..431565bd 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 494d5d11..f132d5d7 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 994ad842..39487fe1 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 2ea76f39..38644c0b 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 00000000..5091a309 --- /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 360c9157..00000000 --- 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 12/54] 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 39487fe1..a1ba8ed8 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 13/54] 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 a383d849..e0ebf622 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 14/54] 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 3c02472f..d202ceed 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 ecc2e484..cab51ea8 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 dc50b265..37ba24e7 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 3b140dfc..ab0a6f10 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 431565bd..f04c0109 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 a1ba8ed8..904c30cc 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 5091a309..c7ab3b7a 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 35a9577d..05e53d92 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 05c1c0c4..b71375f5 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 fd7f6d65..2affa56a 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 d727bc5b..cff5d764 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 15/54] 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 119b17c4..423c2d28 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 16/54] 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 3df0f7c6..937f6c76 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 e0ebf622..9d8d9c6a 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 f04c0109..39d129ba 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 904c30cc..6c506b65 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 c7ab3b7a..2d317fc7 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 17/54] 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 937f6c76..022bbb61 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 9d8d9c6a..00000000 --- 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 00000000..ad6d2244 --- /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 6c506b65..0985014b 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 32582fca580d6adeb6af5d3254a3baa09394cf89 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Tue, 1 Sep 2015 13:19:26 -0700 Subject: [PATCH 18/54] Fixes shouldRasterizeSubnodes Currently, subnodes of nodes marked with shouldRasterizeSubnodes do not get layout calls. This adds the call to layout and changes the __layout method to reference self.bounds instead of _layer.bounds. Also adds support for corner radius when rasterizing subnodes. --- AsyncDisplayKit/ASDisplayNode.mm | 4 ++-- .../Private/ASDisplayNode+AsyncDisplay.mm | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index ab1a6440..7258fc34 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -612,10 +612,10 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) { ASDisplayNodeAssertMainThread(); ASDN::MutexLocker l(_propertyLock); - if (CGRectEqualToRect(_layer.bounds, CGRectZero)) { + if (CGRectEqualToRect(self.bounds, CGRectZero)) { return; // Performing layout on a zero-bounds view often results in frame calculations with negative sizes after applying margins, which will cause measureWithSizeRange: on subnodes to assert. } - _placeholderLayer.frame = _layer.bounds; + _placeholderLayer.frame = self.bounds; [self layout]; [self layoutDidFinish]; } diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index 99167a93..e6551d7a 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -83,6 +83,13 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, if (self.isHidden || self.alpha <= 0.0) { return; } + + BOOL rasterizingFromAscendent = [self __rasterizedContainerNode] != nil; + + // if super node is rasterizing descendents, subnodes will not have had layout calls becase they don't have layers + if (rasterizingFromAscendent) { + [self __layout]; + } // Capture these outside the display block so they are retained. UIColor *backgroundColor = self.backgroundColor; @@ -121,6 +128,11 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync, CGContextTranslateCTM(context, frame.origin.x, frame.origin.y); + //support cornerRadius + if (rasterizingFromAscendent && self.cornerRadius && self.clipsToBounds) { + [[UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:self.cornerRadius] addClip]; + } + // Fill background if any. CGColorRef backgroundCGColor = backgroundColor.CGColor; if (backgroundColor && CGColorGetAlpha(backgroundCGColor) > 0.0) { From 08e31e1c3720abb18d55c9adf06102b22243f092 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Mon, 7 Sep 2015 12:54:42 -0700 Subject: [PATCH 19/54] Created ASCollectionNode with new example project. Eases support for nesting horizontally scrolling elements within a vertical scroller. Further changes will improve the API, and optimize handling of the nested working ranges. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 + AsyncDisplayKit/ASCollectionNode.h | 17 + AsyncDisplayKit/ASCollectionNode.m | 33 ++ AsyncDisplayKit/ASCollectionView.h | 2 + AsyncDisplayKit/ASCollectionView.mm | 7 + AsyncDisplayKit/AsyncDisplayKit.h | 1 + .../Default-568h@2x.png | Bin 0 -> 17520 bytes .../Default-667h@2x.png | Bin 0 -> 18314 bytes .../Default-736h@3x.png | Bin 0 -> 23380 bytes .../HorizontalWithinVerticalScrolling/Podfile | 3 + .../Sample.xcodeproj/project.pbxproj | 358 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/xcschemes/Sample.xcscheme | 88 +++++ .../contents.xcworkspacedata | 1 + .../Sample/AppDelegate.h | 20 + .../Sample/AppDelegate.m | 27 ++ .../Sample/HorizontalScrollCellNode.h | 22 ++ .../Sample/HorizontalScrollCellNode.mm | 100 +++++ .../Sample/Info.plist | 36 ++ .../Sample/RandomCoreGraphicsNode.h | 13 + .../Sample/RandomCoreGraphicsNode.m | 44 +++ .../Sample/ViewController.h | 16 + .../Sample/ViewController.m | 84 ++++ .../Sample/main.m | 20 + 24 files changed, 911 insertions(+) create mode 100644 AsyncDisplayKit/ASCollectionNode.h create mode 100644 AsyncDisplayKit/ASCollectionNode.m create mode 100644 examples/HorizontalWithinVerticalScrolling/Default-568h@2x.png create mode 100644 examples/HorizontalWithinVerticalScrolling/Default-667h@2x.png create mode 100644 examples/HorizontalWithinVerticalScrolling/Default-736h@3x.png create mode 100644 examples/HorizontalWithinVerticalScrolling/Podfile create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.h create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.m create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.h create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/Info.plist create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/ViewController.h create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m create mode 100644 examples/HorizontalWithinVerticalScrolling/Sample/main.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 0809fbf5..e9aa51e5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -139,6 +139,10 @@ 05A6D05B19D0EB64002DD95E /* ASDealloc2MainObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */; }; 05F20AA41A15733C00DCA68A /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */; }; + 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */; }; 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */; }; 204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 204C979D1B362CB3002B1083 /* Default-568h@2x.png */; }; 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -530,6 +534,8 @@ 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASDealloc2MainObject.m; path = ../Details/ASDealloc2MainObject.m; sourceTree = ""; }; 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASSnapshotTestCase.mm; sourceTree = ""; }; 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageProtocols.h; sourceTree = ""; }; + 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionNode.h; sourceTree = ""; }; + 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionNode.m; sourceTree = ""; }; 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = ""; }; 204C979D1B362CB3002B1083 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = ""; }; @@ -738,6 +744,8 @@ children = ( 055F1A3A19ABD43F004DAFF1 /* ASCellNode.h */, AC6456071B0A335000CF11B8 /* ASCellNode.m */, + 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */, + 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.m */, AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */, AC3C4A501A1139C100143C57 /* ASCollectionView.mm */, AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */, @@ -1102,6 +1110,7 @@ 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, + 18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, 058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, 058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */, @@ -1170,6 +1179,7 @@ 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, B35062281B010EFD0018CF92 /* ASRangeHandler.h in Headers */, B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */, + 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */, B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, @@ -1474,6 +1484,7 @@ 464052231A3F83C40061C0BA /* ASFlowLayoutController.mm in Sources */, 058D0A28195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm in Sources */, 0587F9BE1A7309ED00AFF0BA /* ASEditableTextNode.mm in Sources */, + 18C2ED801B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, 058D0A21195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Sources */, ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */, 058D0A25195D050800B7D73C /* UIView+ASConvenience.m in Sources */, @@ -1573,6 +1584,7 @@ 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, + 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */, AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */, 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */, diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h new file mode 100644 index 00000000..6d124b6e --- /dev/null +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -0,0 +1,17 @@ +// +// ASCollectionNode.h +// AsyncDisplayKit +// +// Created by Scott Goodson on 9/5/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@interface ASCollectionNode : ASDisplayNode + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER; + +@property (nonatomic, readonly) ASCollectionView *view; + +@end diff --git a/AsyncDisplayKit/ASCollectionNode.m b/AsyncDisplayKit/ASCollectionNode.m new file mode 100644 index 00000000..2a131bfe --- /dev/null +++ b/AsyncDisplayKit/ASCollectionNode.m @@ -0,0 +1,33 @@ +// +// ASCollectionNode.m +// AsyncDisplayKit +// +// Created by Scott Goodson on 9/5/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASCollectionNode.h" + +@implementation ASCollectionNode + +- (instancetype)init +{ + ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + self = [self initWithCollectionViewLayout:nil]; // Will throw an exception for lacking a UICV Layout. + return nil; +} + +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout +{ + if (self = [super initWithViewBlock:^UIView *{ return [[ASCollectionView alloc] initWithCollectionViewLayout:layout]; }]) { + return self; + } + return nil; +} + +- (ASCollectionView *)view +{ + return (ASCollectionView *)[super view]; +} + +@end diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 5d3a9d9b..47568355 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -27,6 +27,8 @@ */ @interface ASCollectionView : UICollectionView +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout; + @property (nonatomic, weak) id asyncDataSource; @property (nonatomic, weak) id asyncDelegate; // must not be nil diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 54891897..33edd6ed 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -137,6 +137,11 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark Lifecycle. +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout +{ + return [self initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; +} + - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout { return [self initWithFrame:frame collectionViewLayout:layout asyncDataFetching:NO]; @@ -175,6 +180,8 @@ static BOOL _isInterceptedSelector(SEL sel) _maxSizeForNodesConstrainedSize = self.bounds.size; + self.backgroundColor = [UIColor whiteColor]; + [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; return self; diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index b65d144f..e4ba08c8 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -21,6 +21,7 @@ #import #import +#import #import #import diff --git a/examples/HorizontalWithinVerticalScrolling/Default-568h@2x.png b/examples/HorizontalWithinVerticalScrolling/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee80b93937cd9dd79502629b14fe09aa03cacc2 GIT binary patch literal 17520 zcmeI3&2QsG7{;dyhuxq(aR70$vO)rco)4~Hqb-mA2=CIb8^QK*gwP8wCVy+_i!WbB=&euO z!=w19^{!?6gA#W9HYtq<0qu=Ybz>Z0`-H?on{-{TR{Zmu?}~!!)QWeFmfQ*&q~~s* zhveXV_s~8+u`5n-qh6?vEov|zF&4&yz86{JS~2yt=yB346@|1*d{QfJCIbpbtv#XP zheR++hG@%*E|`^)Vkml9c~ekjMU!MrQZ!LfExBSThA{aQ>jipL4V{j)-@H8;jz+a& zFOCCCl18IZX{43>uq!E*N=1@YNmWJKLyXS67>`9Sx|NwseVQb)LpO+B-xCsF-1diY ztyoM3ntdkMH3(({dC`O&r6`SYASoqTS|)PrnI;&9{q)ovTOxfjAYL3%ow8IH^!(V5 zdj5(bXX%v#(>ZCiW@9fs-@#z%&{4c~N)b$uE>%W{X91D+N#qYhn{1uZOS!e|>SMPv zpPUO$NoM7_ld-!(mSi$nx)ib*s?uw<8X>{4A0GOCzn-nKy(vPW(GXs1VcYc*q_0;c z*nd9Rb1TxsF{#tVsEdj$D*B-+TUy1EO;I*2Sj^#R z=5cV0FXfW&oAYsOtK)|Q9M|0e?h+~Rx>af3nCm%PQdYz7`yo9oQrD`|vgVvBU1rvf z7sc4K$xgFQ8%nP0Sc)olh@)wuzTR$&x`VM;Hf>YXY_n{bs#dn!Y6`K{%F7q5o4!3v zw#vlXq1KM0n~q5oQE_zYZ)g>1Jsbid6i*sMS$nsnP**iK4W z-A;A`ajMdV*7<48loOe|IDwa=ocZVEtH&7ih{xJcnN`|rwMpc6;t>wXW|yvs$8Pk@ z@}dTMSEZ!x_uck_9fO)qQO@GMQ+b+k+QN;k1G;mdod%W(l9?2zMP^8s0o3jkq<92c7p$Z}i&2s`As z*nB{i;{rg~A;-n$1F{?!0KyJAE;b*K<+uP4cF1wD`G73P1%R+aj*HC)WH~MXgdK8R zY(5~%aRDIgkmF+W0a=a<0AYt57n={ra$EoiJ7nT2%-^yl9(}cTMBkzP^v2h3(D!cz zdwaiy(D|zfJ@^QrzyG1%zali05&G>uLe}R9z2tv(@5kE+U4MV4xp_GL`Sg5w7{{k8fuN`-gg~6EtdKy$@q3ealPsm_(n_S1wutt$JFzFN)xMF-1^Yl zKS&PRZ`w}KFH<+@u=1!M^4^5hZ;wLioUladup`fJl>Yeo+mhtDjncbTTWyEy?AY5p zkJ#S%_P%p|;?&&I?dEcQWOIW)OQbw>80kV~ynhxlWtYXlAadBoDZiAPi>^NL zy0gi-;FM-AJ$E+pE|H~~T$U|`e1_`$TJ80S(IklWgP_;USJ}=4p|rj(z1*gb=chz*y}FfH5CiynoZ z(1ULtmnQT|F2%kDAJ?(FLDZ*7)9ceCriA`cU70l&dQO*=y&m*}h@Tc~8g*q+b3v6Y zGkeRA6Y4u`tJUNUWzTbMv)ZYeIx}Tvs=93I-HKc_OiS*t8m(1Toz=z=+wG!!&bk#i zgLJEmtzB+S4XSl5!;n{9oyw*`b-6>UrmUK^il*vDw??bk{BY}ne9ro<$m3;>_6mK{ zv;U_`>IE_XGxBbybA%AHk*$y&Essi=Cl+F`4c zIsUhEU>dfjO$yTgGzYWw>l{=6h`CK=a#@px$7$NGR{I`p>s+{xJnqw$@4<_ua8kkN zOJ_ZOc(8fd2=@U+V2j1fk5@J z8HQPu7E)trK3jz+=d5(*t^B#1|0GbRzX|55>h#WYod>gPx=vT%g@XVf;t+9(`G73q z0zkwe;u7-#S;Pf^h(p9B<^!^b3jh&^h)c`|WDyqtA`TIkm=DMzE&xOvA}%o>kVRYo zh&V)CVm=^?xBw7wh`7XjKo)TUAmR{liTQvm;sQX#A>tDA0a?TafQUoHCFTRNhzkG_ zhloqe2V@Z!03r?%mzWR8A}#<#93n0;ACN^{0Ejq5Tw*>Ti?{#~afrCYd_Wd)0U+WK zaf$hWEaCz{#3AAm^8s1J1%QY{#3kkfvWN=+5r;xt%d@v^na^LX9rAZ*rRRS9n7@B3 zIh(s}Le5_zCFIw8gxH@D@_g{o-5>7o_jw0ft+oBp&%b@Yw8WM7 zrH5boo3EvZ_(1|l00|%gB!C2v01`j~NZ=X>eD`35{4^j-PY%DimD+7>Y`4C6{oeh* E06EB-U;qFB literal 0 HcmV?d00001 diff --git a/examples/HorizontalWithinVerticalScrolling/Default-736h@3x.png b/examples/HorizontalWithinVerticalScrolling/Default-736h@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8949cae16a4217842bd8ad813422c6b02e7cafa GIT binary patch literal 23380 zcmeI3&2QsG7{;dvp`~agBn}`rU}c2_5{^Co*u*CBswBI#5^1Zpi0)~3Y)@Ki6WiGC zChY|TCvFHXE5u>%NL;wV0fEGo^J@PC5E5KCaDuU&4|kFdg zdhMfNZ$I1by=i;VuulBQrS}<*{ybMEgw+Y z?`=z+D4~*BH)T)7hSad?*u+K?zba`e))iG(ur6cGRxKNw(&STfR@qT2@%#2p_u6DQ z7PV`KSr*%hG8&EQBfTCa2MV?4Y7lsEkRh;JT_T6Zzgu6CWjm;?#Ukp#wUkVU{u-UaE@^ zqby1fqcet_rOzCg%}K8}8++;b4u?yJPP41G8G;GYrOI^gIHt-DO{1g4qgQXUOS!b{ z>a(CfpPW-pdFIS>r{mxZS)M6n#Zo9|sKu_;?j)3CQL-0B1E*YN+f#&6rz5@GBVG{Z zNMC6weE<1m&#h>eWYl4c(U7q!V`EQKZH#RestsFJD<)-6&Z8IkLH~G(hhf@Aqv}!V z$$PNP%ca`4;^TXEKT3uqbAll`ph_Gbw3K;crRQu(*_~(*CG51Qqqmf0%@tL# z%OtV!#5ekuMW}3fo+cYjqbZZ7=E~GCvFmv%we)@gvDd507p%LH zca(3HiM7wHU9)NG4c*OMb=lB-OLlervW%Ms#tqVRUEP{mSL6%UTS>sm92r#lFw}aGvc-8^S+s2F7KLn=zH_>DnivE{L5fL|(tNwMYt#KUt6;MNm1~M^YZEUo zWsaBc2I{wzQ?2vUnkgr;U~vM^N4fN`$j=^QbVx(dhAOR!UT2%6Q9m1zgsvU1HSw1l zy|g^7;k{c*UiSyVzc33ax&2^sKn+c6P*;_G^K!n@JzsX48kQ}r_EkzOqv5;LIsT_} zU}&~ED@gy*9L(3RcSynm>O0ExvZf7>(zKng_C46vIdva-)Tgc7gQrX3w1O{|&Q|{L zV6(EzN&qR!9d0QLZSw_F_TSIT=isR5-_TU{QE>i$BCV!*>25)XkQ{H}i_^U`z-5-GJRH)BFa2HG_>+sQA=U>Gio()6`~F zT1ic$<#bgZor~I8wz3Cv_M1SN{U}%{tFv3r!#tQ@)5CP-ykHOxh&TjXVm@3JaB)Dy zA>b18;j(~>10oIqmzWQi1za2uaR|7?e7G#&;(&-lz$NCxWdRolL>vMxF&{1qxHur< z5O9h4a9O~`0TG9QOU#GM0xk}SI0Rf`K3o=XaX`c&;1cuUvVe;NA`StUm=Bi)TpSQ_ z2)M+2xGdn}fQUoDCFa9r0T%~E90D#eA1({HI3VH>aEbYFS-`~s5r=?F%!kVYE)Iw| z1YBZ1To!O~K*S;767%7*fQthn4gr^#50?d891w9R#I-tq&6bAj-P#d*iT2)B=M(k< zuH>!n^bk6E38D8sKf00e*l5C8%|00;m9AOHk_01yBIKmZ5;0U!VbfB+Bx0zd!= l00AHX1c1Q*gn;t`y7MJkdHVCOto({Mu5Na}c>U)4e*&NtopJyG literal 0 HcmV?d00001 diff --git a/examples/HorizontalWithinVerticalScrolling/Podfile b/examples/HorizontalWithinVerticalScrolling/Podfile new file mode 100644 index 00000000..6c012e3c --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Podfile @@ -0,0 +1,3 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +pod 'AsyncDisplayKit', :path => '../..' diff --git a/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ba7ab342 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,358 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 05561CFD19D4F94A00CBA93C /* HorizontalScrollCellNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */; }; + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */; }; + 05E2128719D4DB510098F589 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128619D4DB510098F589 /* main.m */; }; + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128919D4DB510098F589 /* AppDelegate.m */; }; + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 05E2128C19D4DB510098F589 /* ViewController.m */; }; + 18C2ED861B9B8CE700F627B3 /* RandomCoreGraphicsNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */; }; + 3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */; }; + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AA19EE274300767484 /* Default-667h@2x.png */; }; + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6C2C82AB19EE274300767484 /* Default-736h@3x.png */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 05561CFB19D4F94A00CBA93C /* HorizontalScrollCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HorizontalScrollCellNode.h; sourceTree = ""; }; + 05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = HorizontalScrollCellNode.mm; sourceTree = ""; }; + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; + 05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05E2128519D4DB510098F589 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 05E2128619D4DB510098F589 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 05E2128819D4DB510098F589 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 05E2128919D4DB510098F589 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 05E2128B19D4DB510098F589 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 05E2128C19D4DB510098F589 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + 18C2ED841B9B8CE700F627B3 /* RandomCoreGraphicsNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RandomCoreGraphicsNode.h; sourceTree = ""; }; + 18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RandomCoreGraphicsNode.m; sourceTree = ""; }; + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; }; + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; }; + C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 05E2127E19D4DB510098F589 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3EC0CDCBA10D483D9F386E5E /* libPods.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05E2127819D4DB510098F589 = { + isa = PBXGroup; + children = ( + 05E2128319D4DB510098F589 /* Sample */, + 05E2128219D4DB510098F589 /* Products */, + 1A943BF0259746F18D6E423F /* Frameworks */, + 1AE410B73DA5C3BD087ACDD7 /* Pods */, + ); + sourceTree = ""; + }; + 05E2128219D4DB510098F589 /* Products */ = { + isa = PBXGroup; + children = ( + 05E2128119D4DB510098F589 /* Sample.app */, + ); + name = Products; + sourceTree = ""; + }; + 05E2128319D4DB510098F589 /* Sample */ = { + isa = PBXGroup; + children = ( + 05E2128819D4DB510098F589 /* AppDelegate.h */, + 05E2128919D4DB510098F589 /* AppDelegate.m */, + 05E2128B19D4DB510098F589 /* ViewController.h */, + 05E2128C19D4DB510098F589 /* ViewController.m */, + 05561CFB19D4F94A00CBA93C /* HorizontalScrollCellNode.h */, + 05561CFC19D4F94A00CBA93C /* HorizontalScrollCellNode.mm */, + 18C2ED841B9B8CE700F627B3 /* RandomCoreGraphicsNode.h */, + 18C2ED851B9B8CE700F627B3 /* RandomCoreGraphicsNode.m */, + 05E2128419D4DB510098F589 /* Supporting Files */, + ); + path = Sample; + sourceTree = ""; + }; + 05E2128419D4DB510098F589 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */, + 6C2C82AA19EE274300767484 /* Default-667h@2x.png */, + 6C2C82AB19EE274300767484 /* Default-736h@3x.png */, + 05E2128519D4DB510098F589 /* Info.plist */, + 05E2128619D4DB510098F589 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 1A943BF0259746F18D6E423F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1AE410B73DA5C3BD087ACDD7 /* Pods */ = { + isa = PBXGroup; + children = ( + C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */, + 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 05E2128019D4DB510098F589 /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */, + 05E2127D19D4DB510098F589 /* Sources */, + 05E2127E19D4DB510098F589 /* Frameworks */, + 05E2127F19D4DB510098F589 /* Resources */, + F012A6F39E0149F18F564F50 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 05E2128119D4DB510098F589 /* Sample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 05E2127919D4DB510098F589 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 05E2128019D4DB510098F589 = { + CreatedOnToolsVersion = 6.0.1; + }; + }; + }; + buildConfigurationList = 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 05E2127819D4DB510098F589; + productRefGroup = 05E2128219D4DB510098F589 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 05E2128019D4DB510098F589 /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 05E2127F19D4DB510098F589 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0585428019D4DBE100606EA6 /* Default-568h@2x.png in Resources */, + 6C2C82AC19EE274300767484 /* Default-667h@2x.png in Resources */, + 6C2C82AD19EE274300767484 /* Default-736h@3x.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + E080B80F89C34A25B3488E26 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + F012A6F39E0149F18F564F50 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 05E2127D19D4DB510098F589 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 18C2ED861B9B8CE700F627B3 /* RandomCoreGraphicsNode.m in Sources */, + 05561CFD19D4F94A00CBA93C /* HorizontalScrollCellNode.mm in Sources */, + 05E2128D19D4DB510098F589 /* ViewController.m in Sources */, + 05E2128A19D4DB510098F589 /* AppDelegate.m in Sources */, + 05E2128719D4DB510098F589 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 05E212A219D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 05E212A319D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 05E212A519D4DB510098F589 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 05E212A619D4DB510098F589 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = Sample/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 05E2127C19D4DB510098F589 /* Build configuration list for PBXProject "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A219D4DB510098F589 /* Debug */, + 05E212A319D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05E212A419D4DB510098F589 /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05E212A519D4DB510098F589 /* Debug */, + 05E212A619D4DB510098F589 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 05E2127919D4DB510098F589 /* Project object */; +} diff --git a/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..a80c0382 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme new file mode 100644 index 00000000..1e14aa03 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata b/examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..d98549fd --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.h b/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.h new file mode 100644 index 00000000..85855277 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.h @@ -0,0 +1,20 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#define UseAutomaticLayout 1 + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.m b/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.m new file mode 100644 index 00000000..1dea563b --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/AppDelegate.m @@ -0,0 +1,27 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "AppDelegate.h" + +#import "ViewController.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + self.window.backgroundColor = [UIColor whiteColor]; + self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.h b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.h new file mode 100644 index 00000000..9d036ce4 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.h @@ -0,0 +1,22 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +/** + * This ASCellNode contains an ASCollectionNode. It intelligently interacts with a containing ASCollectionView or ASTableView, + * to preload and clean up contents as the user scrolls around both vertically and horizontally — in a way that minimizes memory usage. + */ +@interface HorizontalScrollCellNode : ASCellNode + +- (instancetype)initWithElementSize:(CGSize)size; + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm new file mode 100644 index 00000000..9eb2ee9b --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm @@ -0,0 +1,100 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "HorizontalScrollCellNode.h" +#import "RandomCoreGraphicsNode.h" +#import "AppDelegate.h" + +#import + +#import +#import + +static const CGFloat kOuterPadding = 16.0f; +static const CGFloat kInnerPadding = 10.0f; + +@interface HorizontalScrollCellNode () +{ + ASCollectionNode *_collectionNode; + CGSize _elementSize; + ASDisplayNode *_divider; +} + +@end + + +@implementation HorizontalScrollCellNode + +- (instancetype)initWithElementSize:(CGSize)size +{ + if (!(self = [super init])) + return nil; + + _elementSize = size; + + UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; + flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + flowLayout.itemSize = _elementSize; + flowLayout.minimumInteritemSpacing = kInnerPadding; + _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:flowLayout]; + [self addSubnode:_collectionNode]; + + // hairline cell separator + _divider = [[ASDisplayNode alloc] init]; + _divider.backgroundColor = [UIColor lightGrayColor]; + [self addSubnode:_divider]; + + return self; +} + +- (void)didLoad +{ + [super didLoad]; + _collectionNode.view.asyncDelegate = self; + _collectionNode.view.asyncDataSource = self; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return 5; +} + +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; + elementNode.preferredFrameSize = _elementSize; + return elementNode; +} + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + _collectionNode.preferredFrameSize = CGSizeMake(self.bounds.size.width, _elementSize.height); + + ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; + insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, 0.0, kOuterPadding, 0.0); + insetSpec.child = _collectionNode; + + return insetSpec; +} + +// With box model, you don't need to override this method, unless you want to add custom logic. +- (void)layout +{ + [super layout]; + + _collectionNode.view.contentInset = UIEdgeInsetsMake(0.0, kOuterPadding, 0.0, kOuterPadding); + + // Manually layout the divider. + CGFloat pixelHeight = 1.0f / [[UIScreen mainScreen] scale]; + _divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight); +} + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/Info.plist b/examples/HorizontalWithinVerticalScrolling/Sample/Info.plist new file mode 100644 index 00000000..35d84282 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h new file mode 100644 index 00000000..f4324b5d --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.h @@ -0,0 +1,13 @@ +// +// RandomCoreGraphicsNode.h +// Sample +// +// Created by Scott Goodson on 9/5/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@interface RandomCoreGraphicsNode : ASCellNode + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m new file mode 100644 index 00000000..56435a80 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/RandomCoreGraphicsNode.m @@ -0,0 +1,44 @@ +// +// RandomCoreGraphicsNode.m +// Sample +// +// Created by Scott Goodson on 9/5/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "RandomCoreGraphicsNode.h" +#import + +@implementation RandomCoreGraphicsNode + ++ (UIColor *)randomColor +{ + CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0 + CGFloat saturation = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from white + CGFloat brightness = ( arc4random() % 128 / 256.0 ) + 0.5; // 0.5 to 1.0, away from black + return [UIColor colorWithHue:hue saturation:saturation brightness:brightness alpha:1]; +} + ++ (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing +{ + CGFloat locations[3]; + NSMutableArray *colors = [NSMutableArray arrayWithCapacity:3]; + [colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]]; + locations[0] = 0.0; + [colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]]; + locations[1] = 1.0; + [colors addObject:(id)[[RandomCoreGraphicsNode randomColor] CGColor]]; + locations[2] = ( arc4random() % 256 / 256.0 ); + + + CGContextRef ctx = UIGraphicsGetCurrentContext(); + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)colors, locations); + + CGGradientDrawingOptions drawingOptions; + CGContextDrawLinearGradient(ctx, gradient, CGPointZero, CGPointMake(bounds.size.width, bounds.size.height), drawingOptions); + + CGColorSpaceRelease(colorSpace); +} + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.h b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.h new file mode 100644 index 00000000..d0e9200d --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.h @@ -0,0 +1,16 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface ViewController : UIViewController + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m new file mode 100644 index 00000000..b8297f5f --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m @@ -0,0 +1,84 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import + +#import "ViewController.h" +#import "HorizontalScrollCellNode.h" + +@interface ViewController () +{ + ASTableView *_tableView; +} + +@end + +@implementation ViewController + +#pragma mark - +#pragma mark UIViewController. + +- (instancetype)init +{ + if (!(self = [super init])) + return nil; + + _tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.asyncDataSource = self; + _tableView.asyncDelegate = self; + + self.title = @"Horizontal Scrolling Gradients"; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRedo + target:self + action:@selector(reloadEverything)]; + + return self; +} + +- (void)reloadEverything +{ + [_tableView reloadData]; +} + +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.view addSubview:_tableView]; +} + +- (void)viewWillLayoutSubviews +{ + _tableView.frame = self.view.bounds; +} + +- (BOOL)prefersStatusBarHidden +{ + return YES; +} + +#pragma mark - +#pragma mark ASTableView. + +- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +{ + HorizontalScrollCellNode *node = [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)]; + return node; +} + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +{ + return 100; +} + +@end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/main.m b/examples/HorizontalWithinVerticalScrolling/Sample/main.m new file mode 100644 index 00000000..ae948871 --- /dev/null +++ b/examples/HorizontalWithinVerticalScrolling/Sample/main.m @@ -0,0 +1,20 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} From a01d21f509d4036d978e81c3cdf56b4c48879699 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Mon, 7 Sep 2015 14:20:57 -0700 Subject: [PATCH 20/54] Switch to use only layers in the offscreen window used for the render range. Accomodate and document unusual cases in which it is necessary to remove views when clearing backing stores. ASDK layout and display calls don't depend on UIView in any way, so this will achieve the necessary behavior of the render range while eliminating significant UIView-specific overhead from heirarchy manipulation. --- .../Details/ASRangeHandlerRender.mm | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index b9cf41c8..0363abba 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -39,8 +39,12 @@ [node recursivelySetDisplaySuspended:NO]; - // add the node to an off-screen window to force display and preserve its contents - [[self.class workingWindow] addSubnode:node]; + // Add the node's layer to an off-screen window to trigger display and mark its contents as non-volatile. + // Use the layer directly to avoid the substantial overhead of UIView heirarchy manipulations. + // Any view-backed nodes will still create their views in order to assemble the layer heirarchy, and they will + // also assemble a view subtree for the node, but we avoid the much more significant expense triggered by a view + // being added or removed from an onscreen window (responder chain setup, will/DidMoveToWindow: recursive calls, etc) + [[[[self class] workingWindow] layer] addSublayer:node.layer]; } - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType @@ -48,9 +52,36 @@ ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); + // This code is tricky. There are several possible states a node can be in when it reaches this point. + // 1. Layer-backed vs view-backed nodes. AS of this writing, only ASCellNodes arrive here, which are always view-backed — + // but we maintain correctness for all ASDisplayNodes, including layer-backed ones. + // (Note: it would not make sense to pass in a subnode of a rasterized node here, so that is unsupported). + // 2. The node's layer may have been added to the workingWindow previously, or it may have never been added, such as if rangeTuningParameter's leading value is 0. + // 3. The node's layer may not be present in the workingWindow, even if it was previously added. + // This is a common case, as once the node is added to an active cell contentsView (e.g. visible), it is automatically removed from the workingWindow. + // The system does this when addSublayer is called, even if removeFromSuperlayer is never explicitly called. + // 4. Lastly and most unusually, it is possible for a node to be offscreen, completely outside the heirarchy, and yet considered within the working range. + // This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not + // proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling + // -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared + // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:]. + // Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the + // preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards. + // Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality. + [node recursivelySetDisplaySuspended:YES]; - [node.view removeFromSuperview]; - + + if (node.layer.superlayer != [[[self class] workingWindow] layer]) { + // In this case, the node has previously passed through the working range (or it is zero), and it has now fallen outside the working range. + if (![node isLayerBacked]) { + // If the node is view-backed, we need to make sure to remove the view (which is now present in the containing cell contentsView). + // Layer-backed nodes will be fully handled by the unconditional removal below. + [node.view removeFromSuperview]; + } + } + + // At this point, the node's layer may validly be present either in the workingWindow, or in the contentsView of a cell. + [node.layer removeFromSuperlayer]; [node recursivelyClearContents]; } From ed5ebbe4508f95e258a3614445f53ce90503cc0f Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Tue, 8 Sep 2015 09:32:56 -0700 Subject: [PATCH 21/54] update documentation. --- AsyncDisplayKit/ASDisplayNode.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0b639f2a..0b289392 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -92,10 +92,15 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) * * @param c the class, required * @param instance the instance, which may be nil. (If so, the class is inspected instead) + * @remarks The instance value is used only if we suspect the class may be dynamic (because it overloads + * +respondsToSelector: or -respondsToSelector.) In that case we use our "slow path", calling this + * method on each -init and passing the instance value. While this may seem like an unlikely scenario, + * it turns our our own internal tests use a dynamic class, so it's worth capturing this edge case. * * @return ASDisplayNode flags. */ -static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance) { +static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *instance) +{ ASDisplayNodeCAssertNotNil(c, @"class is required"); struct ASDisplayNodeFlags flags = {0}; @@ -119,7 +124,8 @@ static struct ASDisplayNodeFlags GetASDisplayNodeFlags(Class c, ASDisplayNode *i * * @return ASDisplayNodeMethodOverrides. */ -static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { +static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) +{ ASDisplayNodeCAssertNotNil(c, @"class is required"); ASDisplayNodeMethodOverrides overrides = ASDisplayNodeMethodOverrideNone; From a57f701a9e2e303f7f8f6427148071f93e42e987 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Tue, 8 Sep 2015 09:44:54 -0700 Subject: [PATCH 22/54] style fix --- AsyncDisplayKit/ASDisplayNode.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0b289392..a1d670c7 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -204,7 +204,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) #pragma mark - Lifecycle -- (void)_staticInitialize { +- (void)_staticInitialize +{ ASDisplayNodeAssert(NO, @"_staticInitialize must be overridden"); } From ad95a7c3eaee96e91c46d91da30d7d70da2d0fa3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 8 Sep 2015 09:48:59 -0700 Subject: [PATCH 23/54] 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 07f1563c..bc841c20 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 ad6d2244..9e499bd1 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; From 7446578f6f748691154b5f73975f399ac7240364 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:04:06 -0700 Subject: [PATCH 24/54] 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 e9aa51e5..cadfc1ed 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -233,6 +233,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, ); }; }; @@ -580,6 +582,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 = ""; }; @@ -993,6 +996,7 @@ AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, @@ -1046,6 +1050,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 */, @@ -1161,6 +1166,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 05698813..13ee5fdf 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -677,6 +677,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 fd3cfce1..ea18aca9 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 8b784a56..46587d99 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 670c69c9..45fa687f 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 18308953..58dc55f9 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 5ca96ab8..cc6ec941 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 bb200cbf..f599c848 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 ef09aa36..58ecd114 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 4ac01ee0..4ff1ee03 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 f4f02557..05c1c0c4 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 5e5c6f0c..64e9e2c1 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 7825a9b8..b5a126c5 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 3149979d..07243f30 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 38ed2f06..8bd3f3d9 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 eca2637a..585bf946 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 { @@ -54,14 +32,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 @@ -71,16 +54,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 00000000..5618fa6e --- /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 61b72d2c46dbf6a9e1e4ede38682d3868f89b766 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:37:34 -0700 Subject: [PATCH 25/54] 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 0afd654b..2be42824 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -141,7 +141,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 15b3fd6eab3dccc4c8fb1502cef8b740237acc2a Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 09:36:22 -0700 Subject: [PATCH 26/54] 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 + 25 files changed, 370 insertions(+), 188 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 cadfc1ed..44143049 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -235,6 +235,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, ); }; }; @@ -583,6 +587,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 = ""; }; @@ -1001,6 +1007,8 @@ ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, + 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, ); name = Layout; path = ..; @@ -1068,6 +1076,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 */, @@ -1201,6 +1210,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 */, @@ -1461,6 +1471,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 */, @@ -1588,6 +1599,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 */, 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 974bbe12..7fcfbd59 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 13ee5fdf..4b5419cc 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 1b35441c..e8d5b761 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 7ca68f51..68f4f1fd 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 46587d99..d147c767 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 45fa687f..50139a54 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 5e050299..7e52b2c0 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 00000000..0bdee1ad --- /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 00000000..6207ef0d --- /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 f599c848..dc4f100b 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 58ecd114..f277a01b 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 4ff1ee03..8ba86fdc 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 07243f30..ef109d47 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 b23b4fbb..e26d0a02 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 585bf946..b458207b 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -36,17 +36,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 = { @@ -55,16 +44,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 5618fa6e..8e7d74ac 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 2e3d2b3b..bf0239c9 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 84bbdac5..31ff00b5 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 93a2efcb..4112af8e 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 cc3be53d..da4000cd 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 5a7cbd0a..6ea803ba 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 9b6c3d65..2e9c2e78 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 423c2d28..119b17c4 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 293b513d..1e08bbe8 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; From cbaf1789500704c56555d883a8bbe31cbf190ea3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 14:30:50 -0700 Subject: [PATCH 27/54] 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 | 1 - 19 files changed, 344 insertions(+), 129 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 44143049..ce6773d4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -233,12 +233,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, ); }; }; @@ -475,7 +481,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 = ""; }; @@ -586,9 +592,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 = ""; }; @@ -992,6 +1001,7 @@ ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */, ACF6ED111B17843500DA7C62 /* ASLayoutable.h */, + 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */, ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */, ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */, ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */, @@ -1009,6 +1019,8 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, + 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); name = Layout; path = ..; @@ -1085,6 +1097,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 */, @@ -1127,6 +1140,7 @@ 18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.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 */, @@ -1179,6 +1193,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 */, @@ -1267,6 +1282,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; @@ -1472,6 +1488,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 */, @@ -1584,6 +1601,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 4b5419cc..54a2d94b 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) @@ -670,11 +672,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 @@ -1852,6 +1849,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 0bdee1ad..091f078a 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 6207ef0d..df3fc0fb 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 00000000..f7b7f9b9 --- /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 00000000..b0332a80 --- /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 dc4f100b..494d5d11 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 f277a01b..994ad842 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 8ba86fdc..2ea76f39 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 b458207b..e9f47c2e 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" @@ -45,16 +47,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 8e7d74ac..f2e565a7 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 bf0239c9..bf01ff17 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 5a8883ed..d727bc5b 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 00000000..360c9157 --- /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 31ff00b5..95b44504 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 da4000cd..7514507a 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 6ea803ba..45e167af 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 2e9c2e78..fbd89394 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 2be42824..d1f49351 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -136,7 +136,6 @@ static const CGFloat kInnerPadding = 10.0f; { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); _textNode.flexShrink = YES; - _textNode.flexGrow = YES; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; From 8263a9f53ae455a888d3e291b443a94252a5b7fc Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 15:55:41 -0700 Subject: [PATCH 28/54] 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 ce6773d4..b62dc5f9 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -241,10 +241,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, ); }; }; @@ -596,8 +596,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 = ""; }; @@ -1001,7 +1001,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 */, @@ -1140,7 +1140,7 @@ 18C2ED7E1B9B7DE800F627B3 /* ASCollectionNode.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 */, @@ -1282,7 +1282,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 091f078a..3df0f7c6 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 df3fc0fb..a383d849 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 f7b7f9b9..b00e0958 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 b0332a80..431565bd 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 494d5d11..f132d5d7 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 994ad842..39487fe1 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 2ea76f39..38644c0b 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 00000000..5091a309 --- /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 360c9157..00000000 --- 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 36d00273fb8afade37a48b02f6b7da50bc081730 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:49:40 -0700 Subject: [PATCH 29/54] 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 39487fe1..a1ba8ed8 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 84cd80c41d43fbaaaef76032d2fefc1d790f93cb Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:51:15 -0700 Subject: [PATCH 30/54] 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 a383d849..e0ebf622 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 5786bc1b5bc363838ba988507df4945923e657d1 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:11:19 -0700 Subject: [PATCH 31/54] 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 54a2d94b..4dcf41e2 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1849,8 +1849,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 ecc2e484..cab51ea8 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 dc50b265..37ba24e7 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 3b140dfc..ab0a6f10 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 431565bd..f04c0109 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 a1ba8ed8..904c30cc 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 5091a309..c7ab3b7a 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 35a9577d..05e53d92 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 05c1c0c4..b71375f5 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 fd7f6d65..2affa56a 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 d727bc5b..cff5d764 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 2a8b20b102f586652880a13a7870a14883275c5c Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:14:09 -0700 Subject: [PATCH 32/54] 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 119b17c4..423c2d28 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 fca43a651f57f51dd0499ba03a75b1ffc9a6195a Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:41:40 -0700 Subject: [PATCH 33/54] 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 3df0f7c6..937f6c76 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 e0ebf622..9d8d9c6a 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 f04c0109..39d129ba 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 904c30cc..6c506b65 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 c7ab3b7a..2d317fc7 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 b3369ca388491341fe5afd0a04c6e220b887ad78 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 31 Aug 2015 14:19:27 -0700 Subject: [PATCH 34/54] 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 937f6c76..022bbb61 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 9d8d9c6a..00000000 --- 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 00000000..ad6d2244 --- /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 6c506b65..0985014b 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 fe4a2d272f17bccd0bb48f35120bba1e85388f53 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 8 Sep 2015 09:48:59 -0700 Subject: [PATCH 35/54] 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 b62dc5f9..e2e2e1e8 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -235,8 +235,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 */; }; @@ -593,7 +593,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 = ""; }; @@ -1018,7 +1018,7 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, - 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); @@ -1487,7 +1487,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 */, @@ -1617,7 +1617,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 */, 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.m in Sources */, diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index ad6d2244..9e499bd1 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; From 6383b690296422b620426bf266ee6c00859e91f0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 8 Sep 2015 22:30:56 +0300 Subject: [PATCH 36/54] Update ASTableViewTests to detect the case when relayout is trigger during initial configuration. --- AsyncDisplayKitTests/ASTableViewTests.m | 66 +++++++++++++++++++++---- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 4a23a659..39844d6a 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -57,7 +57,8 @@ @end @interface ASTableViewFilledDataSource : NSObject - +/** Calculated by counting how many times a constrained size is asked for the first node on main thread. */ +@property (atomic) int numberOfRelayouts; @end @implementation ASTableViewFilledDataSource @@ -80,6 +81,16 @@ return textCellNode; } +- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +{ + if ([NSThread isMainThread] && indexPath.section == 0 && indexPath.row == 0) { + _numberOfRelayouts++; + } + CGFloat maxWidth = tableView.bounds.size.width; + return ASSizeRangeMake(CGSizeMake(maxWidth, 0), + CGSizeMake(maxWidth, FLT_MAX)); +} + @end @interface ASTableViewTests : XCTestCase @@ -202,34 +213,71 @@ } } -- (void)testRelayoutAllRows +- (void)testRelayoutAllRowsWithNonZeroSizeInitially { - // Initial width of the table view is 0 and all nodes are measured with this size. - ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, 0, 500) + // Initial width of the table view is non-zero and all nodes are measured with this size. + // Any subsequence size change must trigger a relayout. + CGSize tableViewFinalSize = CGSizeMake(100, 500); + // Width and height are swapped so that a later size change will simulate a rotation + ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width) style:UITableViewStylePlain asyncDataFetching:YES]; - CGSize tableViewFinalSize = CGSizeMake(100, 500); ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; + // Trigger layout measurement on all nodes [tableView reloadData]; + [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; +} + +- (void)testRelayoutAllRowsWithZeroSizeInitially +{ + // Initial width of the table view is 0. The first size change is part of the initial config. + // Any subsequence size change after that must trigger a relayout. + CGSize tableViewFinalSize = CGSizeMake(100, 500); + ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectZero + style:UITableViewStylePlain + asyncDataFetching:YES]; + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + + tableView.asyncDelegate = dataSource; + tableView.asyncDataSource = dataSource; + + // Initial configuration + UIView *superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; + [superview addSubview:tableView]; + // Width and height are swapped so that a later size change will simulate a rotation + tableView.frame = CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width); + // Trigger layout measurement on all nodes + [tableView layoutIfNeeded]; + + [self triggerSizeChangeAndAssertRelayoutAllRowsForTableView:tableView newSize:tableViewFinalSize]; +} + +- (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize +{ + XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSizeExpectation"]; + [tableView beginUpdates]; - tableView.frame = CGRectMake(0, 0, tableViewFinalSize.width, tableViewFinalSize.height); + CGRect frame = tableView.frame; + frame.size = newSize; + tableView.frame = frame; [tableView layoutIfNeeded]; - XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; - [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { + int numberOfRelayouts = ((ASTableViewFilledDataSource *)(tableView.asyncDataSource)).numberOfRelayouts; + XCTAssertEqual(numberOfRelayouts, 1); + for (int section = 0; section < NumberOfSections; section++) { for (int row = 0; row < NumberOfRowsPerSection; row++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; ASCellNode *node = [tableView nodeForRowAtIndexPath:indexPath]; - XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewFinalSize.width); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width); } } [nodesMeasuredUsingNewConstrainedSizeExpectation fulfill]; From a8cecbd0ad0a2c806194dabf110d8b1b2aa82ae9 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 8 Sep 2015 22:31:09 +0300 Subject: [PATCH 37/54] Avoid doing relayout during initial configuration of table and collection views. --- AsyncDisplayKit/ASCollectionView.mm | 22 +++++++++++++++++----- AsyncDisplayKit/ASTableView.mm | 22 +++++++++++++++++----- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 33edd6ed..cf54d6fb 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -126,6 +126,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASBatchContext *_batchContext; CGSize _maxSizeForNodesConstrainedSize; + BOOL _ignoreMaxSizeChange; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -179,6 +180,9 @@ static BOOL _isInterceptedSelector(SEL sel) _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; _maxSizeForNodesConstrainedSize = self.bounds.size; + // If the initial size is 0, expect a size change very soon which is part of the initial configuration + // and should not trigger a relayout. + _ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero); self.backgroundColor = [UIColor whiteColor]; @@ -481,14 +485,22 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)layoutSubviews { - [super layoutSubviews]; - if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) { _maxSizeForNodesConstrainedSize = self.bounds.size; - [self performBatchAnimated:NO updates:^{ - [_dataController relayoutAllRows]; - } completion:nil]; + + // First size change occurs during initial configuration. An expensive relayout pass is unnecessary at that time + // and should be avoided, assuming that the initial data loading automatically runs shortly afterward. + if (_ignoreMaxSizeChange) { + _ignoreMaxSizeChange = NO; + } else { + [self performBatchAnimated:NO updates:^{ + [_dataController relayoutAllRows]; + } completion:nil]; + } } + + // To ensure _maxSizeForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last + [super layoutSubviews]; } diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 8948591d..ff482d9c 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -149,6 +149,7 @@ static BOOL _isInterceptedSelector(SEL sel) BOOL _asyncDataSourceImplementsConstrainedSizeForNode; CGFloat _maxWidthForNodesConstrainedSize; + BOOL _ignoreMaxWidthChange; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -203,6 +204,9 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _automaticallyAdjustsContentOffset = NO; _maxWidthForNodesConstrainedSize = self.bounds.size.width; + // If the initial size is 0, expect a size change very soon which is part of the initial configuration + // and should not trigger a relayout. + _ignoreMaxWidthChange = (_maxWidthForNodesConstrainedSize == 0); } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -367,14 +371,22 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)layoutSubviews { - [super layoutSubviews]; - if (_maxWidthForNodesConstrainedSize != self.bounds.size.width) { _maxWidthForNodesConstrainedSize = self.bounds.size.width; - [self beginUpdates]; - [_dataController relayoutAllRows]; - [self endUpdates]; + + // First width change occurs during initial configuration. An expensive relayout pass is unnecessary at that time + // and should be avoided, assuming that the initial data loading automatically runs shortly afterward. + if (_ignoreMaxWidthChange) { + _ignoreMaxWidthChange = NO; + } else { + [self beginUpdates]; + [_dataController relayoutAllRows]; + [self endUpdates]; + } } + + // To ensure _maxWidthForNodesConstrainedSize is up-to-date for every usage, this call to super must be done last + [super layoutSubviews]; } #pragma mark - From 2a030c98410649514a459fe2e08f42dbc7cfc907 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Wed, 9 Sep 2015 09:13:02 -0700 Subject: [PATCH 38/54] First thoughts on fixing the finalLayoutable method. --- AsyncDisplayKit/ASDisplayNode.mm | 5 +++-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 18 ++++++++------- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 23 +++++++++++++++++++- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 4dcf41e2..ce40be71 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -44,6 +44,7 @@ @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize preferredFrameSize = _preferredFrameSize; +@synthesize isFinalLayoutable = _isFinalLayoutable; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -1849,9 +1850,9 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; } -- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec +- (id)finalLayoutable { - return nil; + return self; } @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 0985014b..ac8e7293 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -31,6 +31,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize layoutChildren = _layoutChildren; +@synthesize isFinalLayoutable = _isFinalLayoutable; - (instancetype)init { @@ -49,9 +50,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } -- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; +- (id)finalLayoutable { - return nil; + return self; } - (void)setChild:(id)child; @@ -61,12 +62,13 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { - ASLayoutOptions *layoutOptions = [child layoutOptions]; - - id finalLayoutable = [child finalLayoutableWithParent:self]; - if (finalLayoutable) { - [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; - return finalLayoutable; + if (self.isFinalLayoutable == NO) { + id finalLayoutable = [child finalLayoutable]; + if (finalLayoutable != child) { + ASLayoutOptions *layoutOptions = [child layoutOptions]; + [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; + return finalLayoutable; + } } return child; } diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 2d317fc7..70865dbd 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -12,8 +12,29 @@ @class ASLayoutSpec; @class ASLayoutOptions; +@protocol ASLayoutable; @protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; + +/** + * @abstract A display node cannot implement both calculateSizeThatFits: and layoutSpecThatFits:. This + * method exists to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec just before it is + * added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always inside of an + * ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutable so that it wraps + * self in an inset spec. + * + * Note that any ASLayoutable other than self that is returned MUST set isFinalLayoutable to YES BEFORE + * adding a child. + * + * @return The layoutable that will be added to the parent layout spec. Defaults to self. + */ +- (id)finalLayoutable; + +/** + * A flag to indicate that this ASLayoutable was created in finalLayoutable. This MUST be set to YES + * before adding a child to this layoutable. + */ +@property (nonatomic, assign) BOOL isFinalLayoutable; + @property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; @end From 23497379e4ba9e5e37b5c1e26bdf3ce139a4c1a5 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Wed, 9 Sep 2015 11:51:36 -0700 Subject: [PATCH 39/54] added comment to give guidance if a user is getting crashing in finalLayoutable --- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index ac8e7293..412f194d 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -63,6 +63,20 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { if (self.isFinalLayoutable == NO) { + + // If you are getting recursion crashes here after implementing finalLayoutable, make sure + // that you are setting isFinalLayoutable flag to YES BEFORE adding a child to the new ASLayoutable. + // + // For example: + //- (id)finalLayoutable + //{ + // ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; + // insetSpec.insets = UIEdgeInsetsMake(10,10,10,10); + // insetSpec.isFinalLayoutable = YES; + // [insetSpec setChild:self]; + // return insetSpec; + //} + id finalLayoutable = [child finalLayoutable]; if (finalLayoutable != child) { ASLayoutOptions *layoutOptions = [child layoutOptions]; From b85316d8bc16855a1d32d59395bc49b9c24bb7a9 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Thu, 10 Sep 2015 13:19:13 -0700 Subject: [PATCH 40/54] a couple comment updates --- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 3 ++- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 412f194d..dfcd338a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -65,7 +65,8 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; if (self.isFinalLayoutable == NO) { // If you are getting recursion crashes here after implementing finalLayoutable, make sure - // that you are setting isFinalLayoutable flag to YES BEFORE adding a child to the new ASLayoutable. + // that you are setting isFinalLayoutable flag to YES. This must be one BEFORE adding a child + // to the new ASLayoutable. // // For example: //- (id)finalLayoutable diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 70865dbd..a4aecf3a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -17,14 +17,13 @@ @protocol ASLayoutablePrivate /** - * @abstract A display node cannot implement both calculateSizeThatFits: and layoutSpecThatFits:. This - * method exists to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec just before it is - * added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always inside of an - * ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutable so that it wraps - * self in an inset spec. + * @abstract This method can be used to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec + * just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always + * inside of an ASInsetLayoutSpec, you could subclass ASTextNode and implement finalLayoutable so that it wraps + * itself in an inset spec. * - * Note that any ASLayoutable other than self that is returned MUST set isFinalLayoutable to YES BEFORE - * adding a child. + * Note that any ASLayoutable other than self that is returned MUST set isFinalLayoutable to YES. Make sure + * to do this BEFORE adding a child to the ASLayoutable. * * @return The layoutable that will be added to the parent layout spec. Defaults to self. */ From 4bb84721825bb17632a481b221d9facf14d60bcf Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Thu, 10 Sep 2015 17:29:39 -0700 Subject: [PATCH 41/54] Removed ASBaselineLayoutSpec and made baseline alignment part of ASStackView --- AsyncDisplayKit.xcodeproj/project.pbxproj | 42 +++------- AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h | 60 -------------- .../Layout/ASBaselineLayoutSpec.mm | 80 ------------------- AsyncDisplayKit/Layout/ASBaselineLayoutable.h | 23 ------ AsyncDisplayKit/Layout/ASLayoutOptions.h | 6 +- AsyncDisplayKit/Layout/ASLayoutable.h | 3 +- AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 10 ++- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 2 + AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 33 +++++++- AsyncDisplayKit/Layout/ASStackLayoutable.h | 10 +++ ...ut.h => ASStackBaselinePositionedLayout.h} | 17 +--- ....mm => ASStackBaselinePositionedLayout.mm} | 51 ++++++------ .../Private/ASStackLayoutSpecUtilities.h | 5 ++ .../Private/ASStackPositionedLayout.mm | 2 + 14 files changed, 101 insertions(+), 243 deletions(-) delete mode 100644 AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h delete mode 100644 AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm delete mode 100644 AsyncDisplayKit/Layout/ASBaselineLayoutable.h rename AsyncDisplayKit/Private/{ASBaselinePositionedLayout.h => ASStackBaselinePositionedLayout.h} (52%) rename AsyncDisplayKit/Private/{ASBaselinePositionedLayout.mm => ASStackBaselinePositionedLayout.mm} (82%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index e2e2e1e8..22f2808c 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -221,16 +221,6 @@ 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; }; 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; }; - 9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */; }; - 9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; - 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, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -243,6 +233,10 @@ 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; 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, ); }; }; + 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; }; + 9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; }; + 9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */; settings = {ASSET_TAGS = (); }; }; + 9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */; settings = {ASSET_TAGS = (); }; }; 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 */; }; @@ -586,17 +580,14 @@ 4640521E1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMultidimensionalArrayUtils.h; sourceTree = ""; }; 4640521F1A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMultidimensionalArrayUtils.mm; sourceTree = ""; }; 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = ""; }; - 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselinePositionedLayout.h; sourceTree = ""; }; - 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselinePositionedLayout.mm; sourceTree = ""; }; - 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutable.h; sourceTree = ""; }; - 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 = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; 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 = ""; }; + 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackBaselinePositionedLayout.h; sourceTree = ""; }; + 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; 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 = ""; }; @@ -939,6 +930,8 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */, + 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */, 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */, 2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */, 296A0A2D1A9516B2005ACEAA /* ASBatchFetching.m */, @@ -965,8 +958,6 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, - 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */, - 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -989,7 +980,6 @@ children = ( ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, - 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, @@ -1015,8 +1005,6 @@ 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, - 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, - 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, @@ -1081,8 +1069,8 @@ 464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */, 464052201A3F83C40061C0BA /* ASDataController.h in Headers */, 05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */, + 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, - 9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */, 058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */, 058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1106,7 +1094,6 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, - 9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1133,7 +1120,6 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, - 9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1203,7 +1189,6 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, - 9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1214,7 +1199,6 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, - 9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1259,7 +1243,6 @@ 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, - 9C204A6B1B87803A00313849 /* ASBaselineLayoutable.h in Headers */, 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */, B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1279,6 +1262,7 @@ B35062001B010EFD0018CF92 /* ASEditableTextNode.h in Headers */, B350623A1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, + 9C8221961BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */, B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, @@ -1483,7 +1467,6 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, - 9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1503,6 +1486,7 @@ 205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */, AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */, ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */, + 9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */, 464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */, 055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */, @@ -1525,7 +1509,6 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, - 9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1587,10 +1570,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, - 9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, @@ -1635,6 +1616,7 @@ B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */, B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */, + 9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */, 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, B35062381B010EFD0018CF92 /* ASTextNodeWordKerner.m in Sources */, diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h deleted file mode 100644 index d147c767..00000000 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ /dev/null @@ -1,60 +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 -#import - -typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { - /** No baseline alignment. This is only valid for a vertical stack */ - ASBaselineLayoutBaselineAlignmentNone, - /** Align all children to the first baseline. This is only valid for a horizontal stack */ - ASBaselineLayoutBaselineAlignmentFirst, - /** Align all children to the last baseline. This is useful when a text node wraps and you want to align - to the bottom baseline. This is only valid for a horizontal stack */ - ASBaselineLayoutBaselineAlignmentLast, -}; - -/** - A specialized version of a stack layout that aligns its children on a baseline. This spec only works with - ASBaselineLayoutable children. - - If the spec is created with a horizontal direction, the children will be laid on a common baseline. - 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 - -/** Specifies the direction children are stacked in. */ -@property (nonatomic, assign) ASStackLayoutDirection direction; -/** The amount of space between each child. */ -@property (nonatomic, assign) CGFloat spacing; -/** The amount of space between each child. */ -@property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent; -/** Orientation of children along cross axis */ -@property (nonatomic, assign) ASStackLayoutAlignItems alignItems; -/** The type of baseline alignment */ -@property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment; - -/** - @param direction The direction of the stack view (horizontal or vertical) - @param spacing The spacing between the children - @param baselineAlignment The baseline to align to - @param justifyContent If no children are flexible, this describes how to fill any extra space - @param alignItems Orientation of the children along the cross axis - @param children ASLayoutable children to be positioned. - */ -+ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction - spacing:(CGFloat)spacing - baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment - justifyContent:(ASStackLayoutJustifyContent)justifyContent - alignItems:(ASStackLayoutAlignItems)alignItems - children:(NSArray *)children; - -@end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm deleted file mode 100644 index 50139a54..00000000 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ /dev/null @@ -1,80 +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 "ASBaselineLayoutSpec.h" - -#import -#import - -#import "ASBaseDefines.h" -#import "ASInternalHelpers.h" - -#import "ASLayoutSpecUtilities.h" -#import "ASStackLayoutSpecUtilities.h" -#import "ASStackPositionedLayout.h" -#import "ASStackUnpositionedLayout.h" -#import "ASBaselinePositionedLayout.h" -#import "ASThread.h" - - -@implementation ASBaselineLayoutSpec - -- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children -{ - if (!(self = [super init])) { - return nil; - } - - ASDisplayNodeAssert((direction == ASStackLayoutDirectionHorizontal && baselineAlignment != ASBaselineLayoutBaselineAlignmentNone) || direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); - _direction = direction; - _alignItems = alignItems; - _spacing = spacing; - _justifyContent = justifyContent; - _baselineAlignment = baselineAlignment; - - [self setChildren:children]; - return self; -} - - -+ (instancetype)baselineLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children -{ - return [[ASBaselineLayoutSpec alloc] initWithDirection:direction spacing:spacing baselineAlignment:baselineAlignment justifyContent:justifyContent alignItems:alignItems children:children]; -} - -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize -{ - 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) { - 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); - - const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); - - NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; - - return [ASLayout layoutWithLayoutableObject:self - size:ASSizeRangeClamp(constrainedSize, finalSize) - sublayouts:sublayouts]; -} - -- (void)setChild:(id)child forIdentifier:(NSString *)identifier -{ - ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); -} - -@end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h deleted file mode 100644 index 7e52b2c0..00000000 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h +++ /dev/null @@ -1,23 +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 - -@protocol ASBaselineLayoutable - -/** - * @abstract The distance from the top of the layoutable object to its baseline - */ -@property (nonatomic, readwrite) CGFloat ascender; - -/** - * @abstract The distance from the bottom of the layoutable object to its baseline - */ -@property (nonatomic, readwrite) CGFloat descender; - -@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 022bbb61..36058439 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -12,11 +12,10 @@ @protocol ASLayoutable; -#import #import #import -@interface ASLayoutOptions : NSObject +@interface ASLayoutOptions : NSObject + (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; + (Class)defaultLayoutOptionsClass; @@ -36,9 +35,6 @@ @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; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 38644c0b..8d46caf2 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -12,7 +12,6 @@ #import #import #import -#import #import #import @@ -25,7 +24,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. diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index d7737f7e..82d1aa0d 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -44,7 +44,11 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { /** Center children on cross axis */ ASStackLayoutAlignItemsCenter, /** Expand children to fill cross axis */ - ASStackLayoutAlignItemsStretch + ASStackLayoutAlignItemsStretch, + /** Children align to their first baseline. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsBaselineFirst, + /** Children align to their last baseline. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsBaselineLast, }; /** @@ -62,4 +66,8 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { ASStackLayoutAlignSelfCenter, /** Expand to fill cross axis */ ASStackLayoutAlignSelfStretch, + /** Children align to their first baseline. Only available for horizontal stack nodes */ + ASStackLayoutAlignSelfBaselineFirst, + /** Children align to their last baseline. Only available for horizontal stack nodes */ + ASStackLayoutAlignSelfBaselineLast, }; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index b5a126c5..dd331f14 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -43,6 +43,8 @@ @property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ @property (nonatomic, assign) ASStackLayoutAlignItems alignItems; +/** If YES the vertical spacing between two views is measured from the last baseline of the top view to the top of the bottom view */ +@property (nonatomic, assign) BOOL baselineRelativeArrangement; - (instancetype)init; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index ef109d47..afcb9964 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -17,12 +17,16 @@ #import "ASInternalHelpers.h" #import "ASLayoutSpecUtilities.h" +#import "ASStackBaselinePositionedLayout.h" #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" #import "ASThread.h" @implementation ASStackLayoutSpec +{ + ASDN::RecursiveMutex _propertyLock; +} - (instancetype)init { @@ -72,6 +76,12 @@ _spacing = spacing; } +- (void)setBaselineRelativeArrangement:(BOOL)baselineRelativeArrangement +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _baselineRelativeArrangement = baselineRelativeArrangement; +} + - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); @@ -79,16 +89,33 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; + ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement}; + BOOL needsBaselinePass = _baselineRelativeArrangement || _alignItems == ASStackLayoutAlignItemsBaselineFirst || _alignItems == ASStackLayoutAlignItemsBaselineLast; + std::vector> stackChildren = std::vector>(); for (id child in self.children) { stackChildren.push_back(child); + needsBaselinePass |= child.alignSelf == ASStackLayoutAlignSelfBaselineFirst || child.alignSelf == ASStackLayoutAlignSelfBaselineLast; } 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()]; + + CGSize finalSize = CGSizeZero; + NSArray *sublayouts = nil; + if (needsBaselinePass) { + const auto baselinePositionedLayout = ASStackBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); + ASDN::MutexLocker l(_propertyLock); + self.ascender = baselinePositionedLayout.ascender; + self.descender = baselinePositionedLayout.descender; + + finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); + sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; + } else { + finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); + sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; + } + return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index e26d0a02..5b198492 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -50,4 +50,14 @@ */ @property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; +/** + * @abstract Used for baseline alignment. The distance from the top of the object to its baseline. + */ +@property (nonatomic, readwrite) CGFloat ascender; + +/** + * @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom. + */ +@property (nonatomic, readwrite) CGFloat descender; + @end diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.h similarity index 52% rename from AsyncDisplayKit/Private/ASBaselinePositionedLayout.h rename to AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.h index e3857d5e..6f208e1b 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.h @@ -10,25 +10,16 @@ #import "ASLayout.h" #import "ASDimension.h" -#import "ASBaselineLayoutSpec.h" #import "ASStackPositionedLayout.h" -typedef struct { - /** Describes how the stack will be laid out */ - ASStackLayoutSpecStyle stackLayoutStyle; - - /** The type of baseline alignment */ - ASBaselineLayoutBaselineAlignment baselineAlignment; -} ASBaselineLayoutSpecStyle; - -struct ASBaselinePositionedLayout { +struct ASStackBaselinePositionedLayout { const std::vector sublayouts; const CGFloat crossSize; const CGFloat ascender; const CGFloat descender; /** Given a positioned layout, computes each child position using baseline alignment. */ - static ASBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineLayoutSpecStyle &style, - const ASSizeRange &constrainedSize); + static ASStackBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm similarity index 82% rename from AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm rename to AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm index bf01ff17..aad53bad 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm @@ -8,40 +8,41 @@ * */ -#import "ASBaselinePositionedLayout.h" +#import "ASStackBaselinePositionedLayout.h" #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutOptions.h" -static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, +static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASLayout *layout) { __weak id child = layout.layoutableObject; - switch (style.baselineAlignment) { - case ASBaselineLayoutBaselineAlignmentNone: - return 0; - case ASBaselineLayoutBaselineAlignmentFirst: + switch (style.alignItems) { + case ASStackLayoutAlignItemsBaselineFirst: return child.layoutOptions.ascender; - case ASBaselineLayoutBaselineAlignmentLast: + case ASStackLayoutAlignItemsBaselineLast: return layout.size.height + child.layoutOptions.descender; + default: + return 0; } } -static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, +static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style, const ASLayout *l, const CGFloat maxAscender, const CGFloat maxBaseline) { - if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { + if (style.direction == ASStackLayoutDirectionHorizontal) { __weak id child = l.layoutableObject; - switch (style.baselineAlignment) { - case ASBaselineLayoutBaselineAlignmentFirst: + switch (style.alignItems) { + case ASStackLayoutAlignItemsBaselineFirst: return maxAscender - child.layoutOptions.ascender; - case ASBaselineLayoutBaselineAlignmentLast: + case ASStackLayoutAlignItemsBaselineLast: return maxBaseline - baselineForItem(style, l); - case ASBaselineLayoutBaselineAlignmentNone: + + default: return 0; } } @@ -56,12 +57,10 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, return maxDimension; } -ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineLayoutSpecStyle &style, - const ASSizeRange &constrainedSize) +ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &constrainedSize) { - ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle; - /* Step 1: Look at each child and determine the distance from the top of the child node it's baseline. For example, let's say we have the following two text nodes and want to align them to the first baseline: @@ -108,13 +107,13 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ __weak id child = l.layoutableObject; - p = p + directionPoint(stackStyle.direction, child.layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, child.layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; } else { // otherwise add the stack spacing - p = p + directionPoint(stackStyle.direction, stackStyle.spacing, 0); + p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; @@ -124,10 +123,10 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // If we are a vertical stack, add the item's descender (it is negative) to the offset for the next node. This will ensure we are spacing // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; - if (stackStyle.direction == ASStackLayoutDirectionVertical) { + if (style.direction == ASStackLayoutDirectionVertical) { spacingAfterBaseline = child.layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -143,11 +142,11 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi */ const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](ASLayout *a, ASLayout *b) { - return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); + return maxDimensionForLayout(a, style) < maxDimensionForLayout(b, style); }); - const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, stackStyle); - const auto minCrossSize = crossDimension(stackStyle.direction, constrainedSize.min); - const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); + const auto largestChildCrossSize = it == stackedChildren.end() ? 0 : maxDimensionForLayout(*it, style); + const auto minCrossSize = crossDimension(style.direction, constrainedSize.min); + const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); /* diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 33895b9b..2b073c10 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -15,6 +15,7 @@ typedef struct { CGFloat spacing; ASStackLayoutJustifyContent justifyContent; ASStackLayoutAlignItems alignItems; + BOOL baselineRelativeArrangement; } ASStackLayoutSpecStyle; inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size) @@ -62,6 +63,10 @@ inline ASStackLayoutAlignItems alignment(ASStackLayoutAlignSelf childAlignment, return ASStackLayoutAlignItemsStart; case ASStackLayoutAlignSelfStretch: return ASStackLayoutAlignItemsStretch; + case ASStackLayoutAlignSelfBaselineFirst: + return ASStackLayoutAlignItemsBaselineFirst; + case ASStackLayoutAlignSelfBaselineLast: + return ASStackLayoutAlignItemsBaselineLast; case ASStackLayoutAlignSelfAuto: default: return stackAlignment; diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 95b44504..549e1508 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -25,6 +25,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: return ASFloorPixelValue((crossSize - crossDimension(style.direction, l.layout.size)) / 2); + case ASStackLayoutAlignItemsBaselineFirst: + case ASStackLayoutAlignItemsBaselineLast: case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; From b14e189bfbe0a7a52c8c93bfde452d185ac938d1 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 11 Sep 2015 16:07:18 -0700 Subject: [PATCH 42/54] Addressed comments to LayoutOptions PR --- AsyncDisplayKit/ASDisplayNode.mm | 1 + .../Layout/ASBackgroundLayoutSpec.mm | 11 +++ AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 11 +++ AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 11 +++ AsyncDisplayKit/Layout/ASLayoutOptions.h | 7 +- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 87 +++++++++-------- .../Layout/ASLayoutOptionsPrivate.h | 19 ++-- .../Layout/ASLayoutOptionsPrivate.mm | 15 ++- AsyncDisplayKit/Layout/ASLayoutSpec.h | 27 ++++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 +- AsyncDisplayKit/Layout/ASLayoutable.h | 79 +++++++++++++++- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 11 +++ AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 11 +++ AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 6 ++ AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 22 +++-- .../ASStackBaselinePositionedLayout.mm | 18 ++-- .../Private/ASStackPositionedLayout.mm | 6 +- .../Private/ASStackUnpositionedLayout.mm | 16 ++-- .../ASCenterLayoutSpecSnapshotTests.mm | 2 +- .../ASStackLayoutSpecSnapshotTests.mm | 94 +++++++++---------- 20 files changed, 324 insertions(+), 135 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 8e9e12e3..90aecd38 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -44,6 +44,7 @@ @implementation ASDisplayNode +// these dynamic properties all defined in ASLayoutOptionsPrivate.m @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize preferredFrameSize = _preferredFrameSize; @synthesize isFinalLayoutable = _isFinalLayoutable; diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index ea18aca9..bbeac0b8 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -68,4 +68,15 @@ static NSString * const kBackgroundChildKey = @"kBackgroundChildKey"; return [super childForIdentifier:kBackgroundChildKey]; } +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); +} + +- (NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index 58dc55f9..5e81e1f3 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -90,4 +90,15 @@ return [ASLayout layoutWithLayoutableObject:self size:size sublayouts:@[sublayout]]; } +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); +} + +- (NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index cc6ec941..960360f8 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -109,4 +109,15 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) return [ASLayout layoutWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]]; } +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); +} + +- (NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 36058439..eddb5dae 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -12,8 +12,7 @@ @protocol ASLayoutable; -#import -#import +#import @interface ASLayoutOptions : NSObject @@ -24,8 +23,7 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -- (void)setupDefaults; -- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; +- (void)propogateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions; #pragma mark - ASStackLayoutable @@ -43,5 +41,4 @@ @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; @property (nonatomic, readwrite) CGPoint layoutPosition; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index 9e499bd1..ac426382 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -63,7 +63,20 @@ static Class gDefaultLayoutOptionsClass = nil; { self = [super init]; if (self) { - [self setupDefaults]; + + self.flexBasis = ASRelativeDimensionUnconstrained; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + self.layoutPosition = CGPointZero; + + // The following properties use a default value of 0 which we do not need to assign. + // self.spacingBefore = 0; + // self.spacingAfter = 0; + // self.flexGrow = NO; + // self.flexShrink = NO; + // self.alignSelf = ASStackLayoutAlignSelfAuto; + // self.ascender = 0; + // self.descender = 0; + [self setValuesFromLayoutable:layoutable]; } return self; @@ -73,59 +86,57 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; - [self copyIntoOptions:copy]; + [copy propogateOptionsFromLayoutOptions:self]; return copy; } -- (void)copyIntoOptions:(ASLayoutOptions *)copy +- (void)propogateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions { ASDN::MutexLocker l(_propertyLock); - copy.flexBasis = self.flexBasis; - copy.spacingAfter = self.spacingAfter; - copy.spacingBefore = self.spacingBefore; - copy.flexGrow = self.flexGrow; - copy.flexShrink = self.flexShrink; + self.flexBasis = layoutOptions.flexBasis; + self.spacingAfter = layoutOptions.spacingAfter; + self.spacingBefore = layoutOptions.spacingBefore; + self.flexGrow = layoutOptions.flexGrow; + self.flexShrink = layoutOptions.flexShrink; - copy.ascender = self.ascender; - copy.descender = self.descender; + self.ascender = layoutOptions.ascender; + self.descender = layoutOptions.descender; - copy.sizeRange = self.sizeRange; - copy.layoutPosition = self.layoutPosition; + self.sizeRange = layoutOptions.sizeRange; + self.layoutPosition = layoutOptions.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 +/** + * Given an id, set up layout options that are intrinsically defined by the layoutable. + * + * While this could be done in the layoutable object itself, moving the logic into the ASLayoutOptions class + * allows a custom spec to set up defaults without needing to alter the layoutable itself. For example, + * image you were creating a custom baseline spec that needed ascender/descender. To assign values automatically + * when a text node's attribute string is set, you would need to subclass ASTextNode and assign the values in the + * override of setAttributeString. However, assigning the defaults in an ASLayoutOptions subclass's + * setValuesFromLayoutable allows you to create a custom spec without the need to create a + * subclass of ASTextNode. + * + * @param layoutable The layoutable object to inspect for default intrinsic layout option values + */ - (void)setValuesFromLayoutable:(id)layoutable { ASDN::MutexLocker l(_propertyLock); - if ([layoutable isKindOfClass:[ASTextNode class]]) { - ASTextNode *textNode = (ASTextNode *)layoutable; - 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; self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); self.layoutPosition = displayNode.frame.origin; + + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + NSAttributedString *attributedString = textNode.attributedString; + if (attributedString.length > 0) { + CGFloat screenScale = ASScreenScale(); + self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * screenScale)/screenScale; + self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * screenScale)/screenScale; + } + } + } } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h index b00e0958..34a26da4 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -10,25 +10,24 @@ #import #import - - -@interface ASDisplayNode() -{ - ASLayoutOptions *_layoutOptions; - dispatch_once_t _layoutOptionsInitializeToken; -} -@end +#import @interface ASDisplayNode(ASLayoutOptions) @end -@interface ASLayoutSpec() +@interface ASDisplayNode() { ASLayoutOptions *_layoutOptions; - dispatch_once_t _layoutOptionsInitializeToken; + ASDN::RecursiveMutex _layoutOptionsLock; } @end @interface ASLayoutSpec(ASLayoutOptions) @end +@interface ASLayoutSpec() +{ + ASLayoutOptions *_layoutOptions; + ASDN::RecursiveMutex _layoutOptionsLock; +} +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index 39d129ba..64b3f7b6 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -10,16 +10,27 @@ #import "ASLayoutOptionsPrivate.h" #import +#import "ASThread.h" +/** + * Both an ASDisplayNode and an ASLayoutSpec conform to ASLayoutable. There are several properties + * in ASLayoutable that are used as layoutOptions when a node or spec is used in a layout spec. + * These properties are provided for convenience, as they are forwards to the node or spec's + * ASLayoutOptions class. Instead of duplicating the property forwarding in both classes, we + * create a define that allows us to easily implement the forwards in one place. + * + * If you create a custom layout spec, we recommend this stragety if you decide to extend + * ASDisplayNode and ASLAyoutSpec to provide convenience properties for any options that your + * layoutSpec may require. + */ #define ASLayoutOptionsForwarding \ - (ASLayoutOptions *)layoutOptions\ {\ -dispatch_once(&_layoutOptionsInitializeToken, ^{\ +ASDN::MutexLocker l(_layoutOptionsLock);\ if (_layoutOptions == nil) {\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] initWithLayoutable:self];\ }\ -});\ return _layoutOptions;\ }\ \ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index f132d5d7..1c3c901a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -22,10 +22,37 @@ - (instancetype)init; +/** + * Set child methods + * + * Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * reponsibility of holding on to the spec children. For a layout spec like ASInsetLayoutSpec that + * only requires a single child, the child can be added by calling setChild:. + * + * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) + * a subclass should use the setChild to set the "primary" child. It can then use setChild:forIdentifier: + * to set any other required children. Ideally a subclass would hide this from the user, and use the + * setChildWithIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild + * property that behind the scenes is calling setChild:forIdentifier:. + * + * Finally, a layout spec like ASStackLayoutSpec can take an unknown number of children. In this case, + * the setChildren: method should be used. For good measure, in these layout specs it probably makes + * sense to define setChild: to do something appropriate or to assert. + */ - (void)setChild:(id)child; - (void)setChild:(id)child forIdentifier:(NSString *)identifier; - (void)setChildren:(NSArray *)children; +/** + * Get child methods + * + * There is a corresponding "getChild" method for the above "setChild" methods. If a subclass + * has extra layoutable children, it is recommended to make a corresponding get method for that + * child. For example, the ASBackgroundLayoutSpec responds to backgroundChild. + * + * If a get method is called on a spec that doesn't make sense, then the standard is to assert. + * For example, calling children on an ASInsetLayoutSpec will assert. + */ - (id)child; - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index dfcd338a..c01af7df 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -17,6 +17,7 @@ #import "ASLayout.h" #import "ASLayoutOptions.h" #import "ASLayoutOptionsPrivate.h" +#import "ASThread.h" #import @@ -29,6 +30,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec +// these dynamic properties all defined in ASLayoutOptionsPrivate.m @dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize layoutChildren = _layoutChildren; @synthesize isFinalLayoutable = _isFinalLayoutable; @@ -80,8 +82,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutable]; if (finalLayoutable != child) { - ASLayoutOptions *layoutOptions = [child layoutOptions]; - [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; + [finalLayoutable.layoutOptions propogateOptionsFromLayoutOptions:child.layoutOptions]; return finalLayoutable; } } diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 8d46caf2..7e0e950d 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -20,11 +20,22 @@ @class ASLayoutSpec; /** - * The ASLayoutable protocol declares a method for measuring the layout of an object. A class must implement the method - * 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. + * The ASLayoutable protocol declares a method for measuring the layout of an object. A layout + * is defined by an ASLayout return value, and must specify 1) the size (but not position) of the + * layoutable object, and 2) the size and position of all of its immediate child objects. The tree + * recursion is driven by parents requesting layouts from their children in order to determine their + * size, followed by the parents setting the position of the children once the size is known + * + * The protocol also implements a "family" of Layoutable protocols. These protocols contain layout + * options that can be used for specific layout specs. For example, ASStackLayoutSpec has options + * defining how a layoutable should shrink or grow based upon available space. + * + * These layout options are all stored in an ASLayoutOptions class (that is defined in ASLayoutablePrivate). + * Generally you needn't worry about the layout options class, as the layoutable protocols allow all direct + * access to the options via convenience properties. If you are creating custom layout spec, then you can + * extend the backing layout options class to accomodate any new layout options. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -35,4 +46,64 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; + +#pragma mark - Layout options from the Layoutable Protocols + +#pragma mark - ASStackLayoutable +/** + * @abstract Additional space to place before this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) CGFloat spacingBefore; + +/** + * @abstract Additional space to place after this object in the stacking direction. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) CGFloat spacingAfter; + +/** + * @abstract If the sum of childrens' stack dimensions is less than the minimum size, should this object grow? + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) BOOL flexGrow; + +/** + * @abstract If the sum of childrens' stack dimensions is greater than the maximum size, should this object shrink? + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) BOOL flexShrink; + +/** + * @abstract Specifies the initial size in the stack dimension for this object. + * Default to ASRelativeDimensionUnconstrained. + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; + +/** + * @abstract Orientation of the object along cross axis, overriding alignItems + * Used when attached to a stack layout. + */ +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +/** + * @abstract Used for baseline alignment. The distance from the top of the object to its baseline. + */ +@property (nonatomic, readwrite) CGFloat ascender; + +/** + * @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom. + */ +@property (nonatomic, readwrite) CGFloat descender; + +#pragma mark - 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 layoutPosition; + @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index b71375f5..1fdfd5fc 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -61,4 +61,15 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; return [ASLayout layoutWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts]; } +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); +} + +- (NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 64e9e2c1..eac7464a 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -75,4 +75,15 @@ return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; } +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); +} + +- (NSArray *)children +{ + ASDisplayNodeAssert(NO, @"not supported by this layout spec"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index afcb9964..3f0abf42 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -87,6 +87,12 @@ ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); } +- (id)childForIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children"); + return nil; +} + - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement}; diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index e9f47c2e..2be71b93 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -47,16 +47,15 @@ NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; for (id child in self.children) { - ASLayoutOptions *layoutOptions = child.layoutOptions; CGSize autoMaxSize = { - constrainedSize.max.width - layoutOptions.layoutPosition.x, - constrainedSize.max.height - layoutOptions.layoutPosition.y + constrainedSize.max.width - child.layoutPosition.x, + constrainedSize.max.height - child.layoutPosition.y }; - ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange) + ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) - : ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size); + : ASRelativeSizeRangeResolve(child.sizeRange, size); ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; - sublayout.position = layoutOptions.layoutPosition; + sublayout.position = child.layoutPosition; [sublayouts addObject:sublayout]; } @@ -79,4 +78,15 @@ sublayouts:sublayouts]; } +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); +} + +- (id)childForIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports children"); + return nil; +} + @end diff --git a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm index aad53bad..4e81dd60 100644 --- a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm @@ -20,9 +20,9 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, __weak id child = layout.layoutableObject; switch (style.alignItems) { case ASStackLayoutAlignItemsBaselineFirst: - return child.layoutOptions.ascender; + return child.ascender; case ASStackLayoutAlignItemsBaselineLast: - return layout.size.height + child.layoutOptions.descender; + return layout.size.height + child.descender; default: return 0; } @@ -38,7 +38,7 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style, __weak id child = l.layoutableObject; switch (style.alignItems) { case ASStackLayoutAlignItemsBaselineFirst: - return maxAscender - child.layoutOptions.ascender; + return maxAscender - child.ascender; case ASStackLayoutAlignItemsBaselineLast: return maxBaseline - baselineForItem(style, l); @@ -91,9 +91,9 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A 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 a.layoutableObject.layoutOptions.ascender < b.layoutableObject.layoutOptions.ascender; + return a.layoutableObject.ascender < b.layoutableObject.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.layoutOptions.ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -107,7 +107,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ __weak id child = l.layoutableObject; - p = p + directionPoint(style.direction, child.layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, child.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -124,9 +124,9 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (style.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = child.layoutOptions.descender; + spacingAfterBaseline = child.descender; } - p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -156,7 +156,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A 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 : (*descenderIt).layoutableObject.layoutOptions.descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 549e1508..4d23cf67 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -20,7 +20,7 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - switch (alignment(l.child.layoutOptions.alignSelf, style.alignItems)) { + switch (alignment(l.child.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -51,14 +51,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 *{ - p = p + directionPoint(style.direction, l.child.layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, l.child.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.layoutOptions.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 7514507a..ab2eb3aa 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -26,7 +26,7 @@ static ASLayout *crossChildLayout(const id child, const CGFloat crossMin, const CGFloat crossMax) { - const ASStackLayoutAlignItems alignItems = alignment(child.layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(child.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,7 +76,7 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - const ASStackLayoutAlignItems alignItems = alignment(l.child.layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -112,7 +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 l.child.layoutOptions.flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; }; } } ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - return child.layoutOptions.flexGrow && child.layoutOptions.flexShrink; + return child.flexGrow && child.flexShrink; } /** @@ -294,8 +294,8 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.layoutOptions.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.layoutOptions.flexBasis, stackDimension(style.direction, size)); + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.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 45e167af..10c129f1 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -95,7 +95,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - foregroundNode.layoutOptions.flexGrow = YES; + foregroundNode.flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index fbd89394..2252078d 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -42,8 +42,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - subnode.layoutOptions.flexGrow = flex; - subnode.layoutOptions.flexShrink = flex; + subnode.flexGrow = flex; + subnode.flexShrink = flex; } return subnodes; } @@ -115,7 +115,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - ((ASDisplayNode *)subnodes[1]).layoutOptions.flexShrink = YES; + ((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}}; @@ -205,23 +205,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0; style.spacing = 10; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = -10; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -237,9 +237,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -255,9 +255,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - child1.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(1); - child1.layoutOptions.flexGrow = YES; - child1.layoutOptions.flexShrink = YES; + child1.flexBasis = ASRelativeDimensionMakeWithPercent(1); + child1.flexGrow = YES; + child1.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -272,11 +272,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - subnode1.layoutOptions.flexShrink = YES; + subnode1.flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.layoutOptions.flexShrink = YES; + subnode2.flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -292,7 +292,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.layoutOptions.alignSelf = ASStackLayoutAlignSelfCenter; + subnode2.alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -312,9 +312,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -333,9 +333,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -354,9 +354,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -375,9 +375,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -397,9 +397,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -420,8 +420,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.layoutOptions.flexGrow = YES; - subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); + subnode.flexGrow = YES; + subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -440,12 +440,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.layoutOptions.flexGrow = YES; + 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]).layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + ((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -461,7 +461,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(20); + subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -479,8 +479,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - child2.layoutOptions.flexGrow = YES; - child2.layoutOptions.flexShrink = YES; + child2.flexGrow = YES; + child2.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. @@ -505,13 +505,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.flexShrink = NO; + ((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.flexShrink = YES; + ((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. From 2e3d59a73e7c499ef7f5d513bc126fac8aa5dee2 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 11 Sep 2015 16:13:18 -0700 Subject: [PATCH 43/54] spelling --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 2 +- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 4 ++-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index eddb5dae..5e645731 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -23,7 +23,7 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -- (void)propogateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions; +- (void)propagateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions; #pragma mark - ASStackLayoutable diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index ac426382..b89b59ca 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -86,11 +86,11 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; - [copy propogateOptionsFromLayoutOptions:self]; + [copy propagateOptionsFromLayoutOptions:self]; return copy; } -- (void)propogateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions +- (void)propagateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions { ASDN::MutexLocker l(_propertyLock); self.flexBasis = layoutOptions.flexBasis; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index c01af7df..a91ca103 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -82,7 +82,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutable]; if (finalLayoutable != child) { - [finalLayoutable.layoutOptions propogateOptionsFromLayoutOptions:child.layoutOptions]; + [finalLayoutable.layoutOptions propagateOptionsFromLayoutOptions:child.layoutOptions]; return finalLayoutable; } } From 7dc9b7fc40bed0cf47402f1c4d414c5178b79e6a Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 15 Sep 2015 00:58:48 +0300 Subject: [PATCH 44/54] Fix appledoc warnings. --- AsyncDisplayKit/ASCollectionNode.h | 4 ++++ AsyncDisplayKit/ASTableView.h | 7 +------ AsyncDisplayKit/Details/ASRangeController.h | 6 +++++- AsyncDisplayKit/Layout/ASLayoutOptions.h | 3 +-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h index 6d124b6e..c0489b22 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -8,6 +8,10 @@ #import +/** + * ASCollectionNode is a node based class that wraps an ASCollectionView. It can be used + * as a subnode of another node, and provide room for many (great) features and improvements later on. + */ @interface ASCollectionNode : ASDisplayNode - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout NS_DESIGNATED_INITIALIZER; diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index d4527c04..da000eaf 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -99,16 +99,11 @@ - (void)beginUpdates; /** - * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block. * You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations * to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating * the operations simultaneously. This method is must be called from the main thread. It's important to remeber that the ASTableView will * be processing the updates asynchronously after this call is completed. - * - * @param animated NO to disable all animations. - * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single - * Boolean parameter that contains the value YES if all of the related animations completed successfully or - * NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread. */ - (void)endUpdates; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index 2473f302..9d5bbe57 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -85,7 +85,7 @@ * End updates. * * @param rangeController Sender. - * + * @param animated NO if all animations are disabled. YES otherwise. * @param completion Completion block. */ - (void)rangeController:(ASRangeController * )rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion; @@ -104,6 +104,8 @@ * * @param rangeController Sender. * + * @param nodes Inserted nodes. + * * @param indexPaths Index path of inserted nodes. * * @param animationOptions Animation options. See ASDataControllerAnimationOptions. @@ -115,6 +117,8 @@ * * @param rangeController Sender. * + * @param nodes Deleted nodes. + * * @param indexPaths Index path of deleted nodes. * * @param animationOptions Animation options. See ASDataControllerAnimationOptions. diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 5e645731..15d2f1dd 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -9,11 +9,10 @@ */ #import +#import @protocol ASLayoutable; -#import - @interface ASLayoutOptions : NSObject + (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; From 01be5acece340f0391284347d0e507ae320f9b68 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 14 Sep 2015 20:36:08 -0700 Subject: [PATCH 45/54] added a callback for initWithViewBlock/initWithLayerBlock --- AsyncDisplayKit/ASCellNode.m | 4 +- AsyncDisplayKit/ASDisplayNode.h | 33 +++++++++- AsyncDisplayKit/ASDisplayNode.mm | 61 +++++++++++++------ AsyncDisplayKit/ASEditableTextNode.mm | 4 +- AsyncDisplayKit/ASImageNode.mm | 4 +- AsyncDisplayKit/ASTextNode.mm | 4 +- .../Private/ASDisplayNodeInternal.h | 2 + 7 files changed, 85 insertions(+), 27 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 14b30408..04543ff8 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -31,13 +31,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 7fcfbd59..ca682331 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -19,10 +19,19 @@ * UIView creation block. Used to create the backing view of a new display node. */ typedef UIView *(^ASDisplayNodeViewBlock)(); +/** + * UIView loaded callback block. Used for any additional setup to the view created by viewBlock. + */ +typedef void (^ASDisplayNodeViewLoadedBlock)(UIView *loadedView); + /** * CALayer creation block. Used to create the backing layer of a new display node. */ typedef CALayer *(^ASDisplayNodeLayerBlock)(); +/** + * CALayer loaded callback block. Used for any additional setup to the layer created by layerBlock. + */ +typedef void (^ASDisplayNodeLayerLoadedBlock)(CALayer *layer); /** * An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view @@ -65,6 +74,17 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ - (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock; +/** + * @abstract Alternative initializer with a block to create the backing view. + * + * @param viewBlock The block that will be used to create the backing view. + * @param viewLoadedBlock The block that will be called after the view created by the viewBlock is loaded + * + * @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main + * queue. The view will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock; + /** * @abstract Alternative initializer with a block to create the backing layer. * @@ -73,7 +93,18 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. */ -- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock; +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock; + +/** + * @abstract Alternative initializer with a block to create the backing layer. + * + * @param viewBlock The block that will be used to create the backing layer. + * @param layerLoadedBlock The block that will be called after the layer created by the layerBlock is loaded + * + * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main + * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. + */ +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock; /** @name Properties */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 90aecd38..5f998580 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -256,33 +256,47 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock { - if (!(self = [super init])) - return nil; - - ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView"); - - [self _initializeInstance]; - _viewBlock = viewBlock; - _flags.synchronous = YES; - - return self; + return [self initWithViewBlock:viewBlock viewDidLoadBlock:nil]; } -- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock +- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock { if (!(self = [super init])) return nil; - - ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer"); - + + ASDisplayNodeAssertNotNil(viewBlock, @"should initialize with a valid block that returns a UIView"); + [self _initializeInstance]; - _layerBlock = layerBlock; + _viewBlock = viewBlock; + _viewLoadedBlock = viewLoadedBlock; _flags.synchronous = YES; - _flags.layerBacked = YES; - + return self; } + +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock +{ + return [self initWithLayerBlock:layerBlock layerDidLoadBlock:nil]; +} + +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +{ + if (!(self = [super init])) + return nil; + + ASDisplayNodeAssertNotNil(layerBlock, @"should initialize with a valid block that returns a CALayer"); + + [self _initializeInstance]; + _layerBlock = layerBlock; + _layerLoadedBlock = layerLoadedBlock; + _flags.synchronous = YES; + _flags.layerBacked = YES; + + return self; +} + + - (void)dealloc { ASDisplayNodeAssertMainThread(); @@ -424,7 +438,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) } { TIME_SCOPED(_debugTimeForDidLoad); - [self didLoad]; + [self __didLoad]; } if (self.placeholderEnabled) { @@ -1482,6 +1496,17 @@ static NSInteger incrementIfFound(NSInteger i) { _flags.isMeasured = NO; } +- (void)__didLoad +{ + if (_viewLoadedBlock) { + _viewLoadedBlock(_view); + } + if (_layerLoadedBlock) { + _layerLoadedBlock(_layer); + } + [self didLoad]; +} + - (void)didLoad { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 37196fb2..51d2b040 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -86,13 +86,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index cf90d2f7..09167073 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -96,13 +96,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 68f4f1fd..85059a35 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -149,13 +149,13 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 2ea19602..e4abd526 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -59,7 +59,9 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { NSMutableArray *_subnodes; ASDisplayNodeViewBlock _viewBlock; + ASDisplayNodeViewLoadedBlock _viewLoadedBlock; ASDisplayNodeLayerBlock _layerBlock; + ASDisplayNodeLayerLoadedBlock _layerLoadedBlock; Class _viewClass; Class _layerClass; UIView *_view; From 2f3562fa090e020253611cbf2d5899b0ecbf0130 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 09:11:19 -0700 Subject: [PATCH 46/54] scott's comments --- AsyncDisplayKit/ASCellNode.m | 4 ++-- AsyncDisplayKit/ASDisplayNode.h | 19 +++++++++--------- AsyncDisplayKit/ASDisplayNode.mm | 20 +++++++++---------- AsyncDisplayKit/ASEditableTextNode.mm | 4 ++-- AsyncDisplayKit/ASImageNode.mm | 4 ++-- AsyncDisplayKit/ASTextNode.mm | 4 ++-- .../Private/ASDisplayNodeInternal.h | 3 +-- 7 files changed, 27 insertions(+), 31 deletions(-) diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 04543ff8..80bc3459 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -31,13 +31,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index ca682331..f8a346bd 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -15,23 +15,22 @@ #import +@class ASDisplayNode; + /** * UIView creation block. Used to create the backing view of a new display node. */ typedef UIView *(^ASDisplayNodeViewBlock)(); -/** - * UIView loaded callback block. Used for any additional setup to the view created by viewBlock. - */ -typedef void (^ASDisplayNodeViewLoadedBlock)(UIView *loadedView); /** * CALayer creation block. Used to create the backing layer of a new display node. */ typedef CALayer *(^ASDisplayNodeLayerBlock)(); + /** - * CALayer loaded callback block. Used for any additional setup to the layer created by layerBlock. + * ASDisplayNode loaded callback block. Used for any additional setup after node's layer/view has been loaded */ -typedef void (^ASDisplayNodeLayerLoadedBlock)(CALayer *layer); +typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** * An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view @@ -78,12 +77,12 @@ typedef void (^ASDisplayNodeLayerLoadedBlock)(CALayer *layer); * @abstract Alternative initializer with a block to create the backing view. * * @param viewBlock The block that will be used to create the backing view. - * @param viewLoadedBlock The block that will be called after the view created by the viewBlock is loaded + * @param didLoadBlock The block that will be called after the view created by the viewBlock is loaded * * @return An ASDisplayNode instance that loads its view with the given block that is guaranteed to run on the main * queue. The view will render synchronously and -layout and touch handling methods on the node will not be called. */ -- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock; +- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock; /** * @abstract Alternative initializer with a block to create the backing layer. @@ -99,12 +98,12 @@ typedef void (^ASDisplayNodeLayerLoadedBlock)(CALayer *layer); * @abstract Alternative initializer with a block to create the backing layer. * * @param viewBlock The block that will be used to create the backing layer. - * @param layerLoadedBlock The block that will be called after the layer created by the layerBlock is loaded + * @param didLoadBlock The block that will be called after the layer created by the layerBlock is loaded * * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. */ -- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock; +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock; /** @name Properties */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 5f998580..323ade43 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -256,10 +256,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock { - return [self initWithViewBlock:viewBlock viewDidLoadBlock:nil]; + return [self initWithViewBlock:viewBlock didLoadBlock:nil]; } -- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock +- (id)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { if (!(self = [super init])) return nil; @@ -268,7 +268,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _initializeInstance]; _viewBlock = viewBlock; - _viewLoadedBlock = viewLoadedBlock; + _nodeLoadedBlock = didLoadBlock; _flags.synchronous = YES; return self; @@ -277,10 +277,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) - (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock { - return [self initWithLayerBlock:layerBlock layerDidLoadBlock:nil]; + return [self initWithLayerBlock:layerBlock didLoadBlock:nil]; } -- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +- (id)initWithLayerBlock:(ASDisplayNodeLayerBlock)layerBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { if (!(self = [super init])) return nil; @@ -289,7 +289,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self _initializeInstance]; _layerBlock = layerBlock; - _layerLoadedBlock = layerLoadedBlock; + _nodeLoadedBlock = didLoadBlock; _flags.synchronous = YES; _flags.layerBacked = YES; @@ -1498,11 +1498,9 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)__didLoad { - if (_viewLoadedBlock) { - _viewLoadedBlock(_view); - } - if (_layerLoadedBlock) { - _layerLoadedBlock(_layer); + if (_nodeLoadedBlock) { + _nodeLoadedBlock(self); + _nodeLoadedBlock = nil; } [self didLoad]; } diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 51d2b040..c2ecde2b 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -86,13 +86,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index 09167073..dce8d0f0 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -96,13 +96,13 @@ return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 85059a35..d688de0d 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -149,13 +149,13 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) return self; } -- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock layerDidLoadBlock:(ASDisplayNodeLayerLoadedBlock)layerLoadedBlock +- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; } -- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock viewDidLoadBlock:(ASDisplayNodeViewLoadedBlock)viewLoadedBlock +- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(ASDisplayNodeDidLoadBlock)didLoadBlock { ASDisplayNodeAssertNotSupported(); return nil; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index e4abd526..7066159c 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -59,9 +59,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { NSMutableArray *_subnodes; ASDisplayNodeViewBlock _viewBlock; - ASDisplayNodeViewLoadedBlock _viewLoadedBlock; ASDisplayNodeLayerBlock _layerBlock; - ASDisplayNodeLayerLoadedBlock _layerLoadedBlock; + ASDisplayNodeDidLoadBlock _nodeLoadedBlock; Class _viewClass; Class _layerClass; UIView *_view; From 2d30a81a75ddc97151f3b5c57867edc97ac59c1e Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 11:03:40 -0700 Subject: [PATCH 47/54] added documentation --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 50 ++++++++++++++++++-- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 5 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 +- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 8 ++++ AsyncDisplayKit/Layout/ASStackLayoutable.h | 3 ++ AsyncDisplayKit/Layout/ASStaticLayoutable.h | 3 ++ 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 15d2f1dd..74352a34 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -13,16 +13,58 @@ @protocol ASLayoutable; +/** + * A store for all of the options defined by ASLayoutSpec subclasses. All implementors of ASLayoutable own a + * ASLayoutOptions. When certain layoutSpecs need option values, they are read from this class. + * + * Unless you wish to create a custom layout spec, ASLayoutOptions can largerly be ignored. Instead you can access + * the layout option properties exposed in ASLayoutable directly, which will set the values in ASLayoutOptions + * behind the scenes. + */ @interface ASLayoutOptions : NSObject +/** + * Sets the class name for the ASLayoutOptions subclasses that will be created when a node or layoutSpec's options + * are first accessed. + * + * If you create a custom layoutSpec that includes new options, you will want to subclass ASLayoutOptions to add + * the new layout options for your layoutSpec(s). In order to make sure your subclass is created instead of an + * instance of ASLayoutOptions, call setDefaultLayoutOptionsClass: early in app launch (applicationDidFinishLaunching:) + * with your subclass's class. + * + * @param defaultLayoutOptionsClass The class of ASLayoutOptions that will be lazily created for a node or layout spec. + */ + (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; + +/** + * @return the Class of ASLayoutOptions that will be created for a node or layoutspec. Defaults to [ASLayoutOptions class]; + */ + (Class)defaultLayoutOptionsClass; -- (instancetype)initWithLayoutable:(id)layoutable; -- (void)setValuesFromLayoutable:(id)layoutable; - #pragma mark - Subclasses should implement these! -- (void)propagateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions; +/** + * Initializes a new ASLayoutOptions using the given layoutable to assign any intrinsic option values. + * This init function sets a sensible default value for each layout option. If you create a subclass of + * ASLayoutOptions, your subclass should do the same. + * + * @param layoutable The layoutable that will own these options. The layoutable will be used to set any intrinsic + * layoutOptions. For example, if the layoutable is an ASTextNode the ascender/descender values will get set. + * + * @return a new instance of ASLayoutOptions + */ +- (instancetype)initWithLayoutable:(id)layoutable; + +/** + * Copies the values of layoutOptions into self. This is useful when placing a layoutable inside of another. Consider + * an ASTextNode that you want to align to the baseline by putting it in an ASStackLayoutSpec. Before that, you want + * to inset the ASTextNode by placing it in an ASInsetLayoutSpec. An ASInsetLayoutSpec will not have any information + * about the ASTextNode's ascender/descender unless we copy over the layout options from ASTextNode to ASInsetLayoutSpec. + * This is done automatically and should not need to be called directly. It is listed here to make sure that any + * ASLayoutOptions subclass implements the method. + * + * @param layoutOptions The layoutOptions to copy from + */ +- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; #pragma mark - ASStackLayoutable diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index b89b59ca..67481cae 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -86,11 +86,11 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; - [copy propagateOptionsFromLayoutOptions:self]; + [copy copyIntoOptions:self]; return copy; } -- (void)propagateOptionsFromLayoutOptions:(ASLayoutOptions *)layoutOptions +- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions { ASDN::MutexLocker l(_propertyLock); self.flexBasis = layoutOptions.flexBasis; @@ -125,7 +125,6 @@ static Class gDefaultLayoutOptionsClass = nil; if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.layoutPosition = displayNode.frame.origin; if ([layoutable isKindOfClass:[ASTextNode class]]) { ASTextNode *textNode = (ASTextNode *)layoutable; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index a91ca103..46e56dff 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -82,7 +82,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutable]; if (finalLayoutable != child) { - [finalLayoutable.layoutOptions propagateOptionsFromLayoutOptions:child.layoutOptions]; + [finalLayoutable.layoutOptions copyIntoOptions:child.layoutOptions]; return finalLayoutable; } } diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index a4aecf3a..f52dd54a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -14,6 +14,10 @@ @class ASLayoutOptions; @protocol ASLayoutable; +/** + * The base protocol for ASLayoutable. Generally the methods/properties in this class do not need to be + * called by the end user and are only called internally. However, there may be a case where the methods are useful. + */ @protocol ASLayoutablePrivate /** @@ -35,5 +39,9 @@ */ @property (nonatomic, assign) BOOL isFinalLayoutable; + +/** + * The class that holds all of the layoutOptions set on an ASLayoutable. + */ @property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 5b198492..3ebc9304 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -11,6 +11,9 @@ #import #import +/** + * Layout options that can be defined for an ASLayoutable being added to a ASStackLayoutSpec. + */ @protocol ASStackLayoutable /** diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index f2e565a7..e0325cfc 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -10,6 +10,9 @@ #import +/** + * Layout options that can be defined for an ASLayoutable being added to a ASStaticLayoutSpec. + */ @protocol ASStaticLayoutable /** From 3b4055fcd3fa194ed61bf2e0fcd763aa81566894 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 12:38:41 -0700 Subject: [PATCH 48/54] few more doc changes. --- AsyncDisplayKit/ASDisplayNode.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index f8a346bd..11a138ea 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -28,7 +28,7 @@ typedef UIView *(^ASDisplayNodeViewBlock)(); typedef CALayer *(^ASDisplayNodeLayerBlock)(); /** - * ASDisplayNode loaded callback block. Used for any additional setup after node's layer/view has been loaded + * ASDisplayNode loaded callback block. This block is called BEFORE the -didLoad method and is always called on the main thread. */ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); @@ -87,7 +87,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** * @abstract Alternative initializer with a block to create the backing layer. * - * @param viewBlock The block that will be used to create the backing layer. + * @param layerBlock The block that will be used to create the backing layer. * * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main * queue. The layer will render synchronously and -layout and touch handling methods on the node will not be called. @@ -97,7 +97,7 @@ typedef void (^ASDisplayNodeDidLoadBlock)(ASDisplayNode *node); /** * @abstract Alternative initializer with a block to create the backing layer. * - * @param viewBlock The block that will be used to create the backing layer. + * @param layerBlock The block that will be used to create the backing layer. * @param didLoadBlock The block that will be called after the layer created by the layerBlock is loaded * * @return An ASDisplayNode instance that loads its layer with the given block that is guaranteed to run on the main From eee60fb31b655c5293eeae87421716b1a2b0fede Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 17:10:55 -0700 Subject: [PATCH 49/54] Move ASLayoutOptionsPrivate to the private folder fixes: https://github.com/facebook/AsyncDisplayKit/issues/654 --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++------ AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 1 - .../{Layout => Private}/ASLayoutOptionsPrivate.h | 0 3 files changed, 6 insertions(+), 7 deletions(-) rename AsyncDisplayKit/{Layout => Private}/ASLayoutOptionsPrivate.h (100%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 22f2808c..1a032c42 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -227,10 +227,10 @@ 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; + 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; 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, ); }; }; 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; }; @@ -583,8 +583,8 @@ 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.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 = ""; }; + 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackBaselinePositionedLayout.h; sourceTree = ""; }; 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = ""; }; @@ -930,6 +930,7 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */, 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */, 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */, 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */, @@ -1007,7 +1008,6 @@ ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, - 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); name = Layout; @@ -1063,6 +1063,7 @@ ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */, + 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */, ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */, 291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */, 464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */, @@ -1085,7 +1086,6 @@ 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 */, @@ -1179,7 +1179,6 @@ 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 */, @@ -1254,6 +1253,7 @@ 34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */, B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */, 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */, + 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */, B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */, 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 2be71b93..8e8435c9 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -12,7 +12,6 @@ #import "ASLayoutSpecUtilities.h" #import "ASLayoutOptions.h" -#import "ASLayoutOptionsPrivate.h" #import "ASInternalHelpers.h" #import "ASLayout.h" #import "ASStaticLayoutable.h" diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h rename to AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h From 9c8aaeb17e72b65702733c26269e340be280a686 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 17:10:55 -0700 Subject: [PATCH 50/54] Move ASLayoutOptionsPrivate to the private folder fixes: https://github.com/facebook/AsyncDisplayKit/issues/654 --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++------ AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 1 - .../{Layout => Private}/ASLayoutOptionsPrivate.h | 0 3 files changed, 6 insertions(+), 7 deletions(-) rename AsyncDisplayKit/{Layout => Private}/ASLayoutOptionsPrivate.h (100%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 22f2808c..1a032c42 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -227,10 +227,10 @@ 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 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 */; }; 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; + 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; 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, ); }; }; 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; }; @@ -583,8 +583,8 @@ 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.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 = ""; }; + 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackBaselinePositionedLayout.h; sourceTree = ""; }; 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = ""; }; @@ -930,6 +930,7 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */, 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */, 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */, 296A0A2C1A9516B2005ACEAA /* ASBatchFetching.h */, @@ -1007,7 +1008,6 @@ ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, - 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); name = Layout; @@ -1063,6 +1063,7 @@ ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, ACF6ED2F1B17843500DA7C62 /* ASStackLayoutSpec.h in Headers */, + 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */, ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */, 291B63FB1AA53A7A000A71B3 /* ASScrollDirection.h in Headers */, 464052221A3F83C40061C0BA /* ASFlowLayoutController.h in Headers */, @@ -1085,7 +1086,6 @@ 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 */, @@ -1179,7 +1179,6 @@ 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 */, @@ -1254,6 +1253,7 @@ 34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */, B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */, 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */, + 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */, B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */, 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 2be71b93..8e8435c9 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -12,7 +12,6 @@ #import "ASLayoutSpecUtilities.h" #import "ASLayoutOptions.h" -#import "ASLayoutOptionsPrivate.h" #import "ASInternalHelpers.h" #import "ASLayout.h" #import "ASStaticLayoutable.h" diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h rename to AsyncDisplayKit/Private/ASLayoutOptionsPrivate.h From b0948ee6ae65d53b1009947b9f1da7a06fbb826b Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 15 Sep 2015 17:18:08 -0700 Subject: [PATCH 51/54] Changed target membership of ASLayoutOptionsPrivate.h to Private from Public. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 1a032c42..cb478864 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -229,8 +229,8 @@ 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; - 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; - 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ASSET_TAGS = (); }; }; + 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 9C65A72B1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 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, ); }; }; 9C8221951BA237B80037F19A /* ASStackBaselinePositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */; settings = {ASSET_TAGS = (); }; }; From 2d575fcafc4770c905a11cc631a16a9474c3d3ee Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 15 Sep 2015 22:26:11 +0300 Subject: [PATCH 52/54] Relayout table view cell nodes if there is a mismatch between content view size and the node's constrained size - Above is the generic case. Correctly handling it means relayout when the table view enters or leaves editing mode is solved as well. - Async data source API removal: In a table view, cell nodes should always fill its content view and table view widths. Thus async data source can no longer provide custom constrained size for cell nodes. This removal allows table view to better handle relayout. - Some more tests are added to ASTableViewTests to check against use cases handled in this diff. --- AsyncDisplayKit/ASTableView.h | 11 -- AsyncDisplayKit/ASTableView.mm | 61 +++++---- AsyncDisplayKitTests/ASTableViewTests.m | 174 +++++++++++++++++++++--- 3 files changed, 191 insertions(+), 55 deletions(-) diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index da000eaf..ed3ecd62 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -266,17 +266,6 @@ @optional -/** - * Provides the constrained size range for measuring the node at the index path. - * - * @param tableView The sender. - * - * @param indexPath The index path of the node. - * - * @returns A constrained size range for layout the node at this index path. - */ -- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; - /** * Indicator to lock the data source for data fetching in async mode. * We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index ff482d9c..530b7687 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -16,6 +16,7 @@ #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) @@ -104,22 +105,30 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark ASCellNode<->UITableViewCell bridging. +@class _ASTableViewCell; + @protocol _ASTableViewCellDelegate -- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state; +- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell; @end @interface _ASTableViewCell : UITableViewCell @property (nonatomic, weak) id<_ASTableViewCellDelegate> delegate; -@property (nonatomic) NSIndexPath *indexPath; +@property (nonatomic, weak) ASCellNode *node; @end @implementation _ASTableViewCell // TODO add assertions to prevent use of view-backed UITableViewCell properties (eg .textLabel) +- (void)layoutSubviews +{ + [_delegate willLayoutSubviewsOfTableViewCell:self]; + [super layoutSubviews]; +} + - (void)didTransitionToState:(UITableViewCellStateMask)state { + [self setNeedsLayout]; [super didTransitionToState:state]; - [_delegate tableViewCell:self atIndexPath:_indexPath didTransitionToState:state]; } @end @@ -146,8 +155,6 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; - BOOL _asyncDataSourceImplementsConstrainedSizeForNode; - CGFloat _maxWidthForNodesConstrainedSize; BOOL _ignoreMaxWidthChange; } @@ -271,12 +278,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { super.dataSource = nil; _asyncDataSource = nil; _proxyDataSource = nil; - _asyncDataSourceImplementsConstrainedSizeForNode = NO; } else { _asyncDataSource = asyncDataSource; _proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; super.dataSource = (id)_proxyDataSource; - _asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(tableView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0); } } @@ -507,7 +512,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; [_rangeController configureContentView:cell.contentView forCellNode:node]; - cell.indexPath = indexPath; + cell.node = node; cell.backgroundColor = node.backgroundColor; cell.selectionStyle = node.selectionStyle; @@ -798,11 +803,6 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - if (_asyncDataSourceImplementsConstrainedSizeForNode) { - return [_asyncDataSource tableView:self constrainedSizeForNodeAtIndexPath:indexPath]; - } - - // Default size range return ASSizeRangeMake(CGSizeMake(_maxWidthForNodesConstrainedSize, 0), CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); } @@ -845,22 +845,31 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { #pragma mark - _ASTableViewCellDelegate -- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state +- (void)willLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell { - [self beginUpdates]; - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + CGFloat contentViewWidth = tableViewCell.contentView.bounds.size.width; + ASCellNode *node = tableViewCell.node; + ASSizeRange constrainedSize = node.constrainedSizeForCalculatedLayout; - ASSizeRange constrainedSize = [self dataController:_dataController constrainedSizeForNodeAtIndexPath:indexPath]; - if (state != UITableViewCellStateDefaultMask) { - // Edit control or delete confirmation was shown and size of content view was changed. - // The new size should be taken into consideration. - constrainedSize.min.width = MIN(cell.contentView.frame.size.width, constrainedSize.min.width); - constrainedSize.max.width = MIN(cell.contentView.frame.size.width, constrainedSize.max.width); + // Table view cells should always fill its content view width. + // Normally the content view width equals to the constrained size width (which equals to the table view width). + // If there is a mismatch between these values, for example after the table view entered or left editing mode, + // content view width is preferred and used to re-measure the cell node. + if (contentViewWidth != constrainedSize.max.width) { + constrainedSize.min.width = contentViewWidth; + constrainedSize.max.width = contentViewWidth; + + // Re-measurement is done on main to ensure thread affinity. In the worst case, this is as fast as UIKit's implementation. + // + // Unloaded nodes *could* be re-measured off the main thread, but only with the assumption that content view width + // is the same for all cells (because there is no easy way to get that individual value before the node being assigned to a _ASTableViewCell). + // Also, in many cases, some nodes may not need to be re-measured at all, such as when user enters and then immediately leaves editing mode. + // To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main. + [self beginUpdates]; + CGSize calculatedSize = [[node measureWithSizeRange:constrainedSize] size]; + node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height); + [self endUpdates]; } - - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0, 0, node.calculatedSize.width, node.calculatedSize.height); - [self endUpdates]; } @end diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 39844d6a..1d074ac3 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -9,6 +9,7 @@ #import #import "ASTableView.h" +#import "ASDisplayNode+Subclasses.h" #define NumberOfSections 10 #define NumberOfRowsPerSection 20 @@ -56,9 +57,24 @@ @end +@interface ASTestTextCellNode : ASTextCellNode +/** Calculated by counting how many times -layoutSpecThatFits: is called on the main thread. */ +@property (atomic) int numberOfLayoutsOnMainThread; +@end + +@implementation ASTestTextCellNode + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + if ([NSThread isMainThread]) { + _numberOfLayoutsOnMainThread++; + } + return [super layoutSpecThatFits:constrainedSize]; +} + +@end + @interface ASTableViewFilledDataSource : NSObject -/** Calculated by counting how many times a constrained size is asked for the first node on main thread. */ -@property (atomic) int numberOfRelayouts; @end @implementation ASTableViewFilledDataSource @@ -75,22 +91,12 @@ - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath { - ASTextCellNode *textCellNode = [ASTextCellNode new]; + ASTestTextCellNode *textCellNode = [ASTestTextCellNode new]; textCellNode.text = indexPath.description; return textCellNode; } -- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath -{ - if ([NSThread isMainThread] && indexPath.section == 0 && indexPath.row == 0) { - _numberOfRelayouts++; - } - CGFloat maxWidth = tableView.bounds.size.width; - return ASSizeRangeMake(CGSizeMake(maxWidth, 0), - CGSizeMake(maxWidth, FLT_MAX)); -} - @end @interface ASTableViewTests : XCTestCase @@ -260,7 +266,7 @@ - (void)triggerSizeChangeAndAssertRelayoutAllRowsForTableView:(ASTableView *)tableView newSize:(CGSize)newSize { - XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSizeExpectation"]; + XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; [tableView beginUpdates]; @@ -270,13 +276,11 @@ [tableView layoutIfNeeded]; [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { - int numberOfRelayouts = ((ASTableViewFilledDataSource *)(tableView.asyncDataSource)).numberOfRelayouts; - XCTAssertEqual(numberOfRelayouts, 1); - for (int section = 0; section < NumberOfSections; section++) { for (int row = 0; row < NumberOfRowsPerSection; row++) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; - ASCellNode *node = [tableView nodeForRowAtIndexPath:indexPath]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, newSize.width); } } @@ -290,4 +294,138 @@ }]; } +- (void)testRelayoutVisibleRowsWhenEditingModeIsChanged +{ + CGSize tableViewSize = CGSizeMake(100, 500); + ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height) + style:UITableViewStylePlain + asyncDataFetching:YES]; + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + + tableView.asyncDelegate = dataSource; + tableView.asyncDataSource = dataSource; + + XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; + [tableView reloadDataWithCompletion:^{ + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + } + } + [reloadDataExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; + + NSArray *visibleNodes = [tableView visibleNodes]; + XCTAssertGreaterThan(visibleNodes.count, 0); + + // Cause table view to enter editing mode. + // Visibile nodes should be re-measured on main thread with the new (smaller) content view width. + // Other nodes are untouched. + XCTestExpectation *relayoutAfterEnablingEditingExpectation = [self expectationWithDescription:@"relayoutAfterEnablingEditing"]; + [tableView beginUpdates]; + [tableView setEditing:YES]; + [tableView endUpdatesAnimated:YES completion:^(BOOL completed) { + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + if ([visibleNodes containsObject:node]) { + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); + XCTAssertLessThan(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + } else { + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + } + } + } + [relayoutAfterEnablingEditingExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; + + // Cause table view to leave editing mode. + // Visibile nodes should be re-measured again. + // All nodes should have max constrained width equals to the table view width. + XCTestExpectation *relayoutAfterDisablingEditingExpectation = [self expectationWithDescription:@"relayoutAfterDisablingEditing"]; + [tableView beginUpdates]; + [tableView setEditing:NO]; + [tableView endUpdatesAnimated:YES completion:^(BOOL completed) { + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + BOOL visible = [visibleNodes containsObject:node]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, visible ? 2: 0); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + } + } + [relayoutAfterDisablingEditingExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + +- (void)testRelayoutRowsAfterEditingModeIsChangedAndTheyBecomeVisible +{ + CGSize tableViewSize = CGSizeMake(100, 500); + ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height) + style:UITableViewStylePlain + asyncDataFetching:YES]; + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + + tableView.asyncDelegate = dataSource; + tableView.asyncDataSource = dataSource; + + XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; + [tableView reloadDataWithCompletion:^{ + for (int section = 0; section < NumberOfSections; section++) { + for (int row = 0; row < NumberOfRowsPerSection; row++) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section]; + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:indexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 0); + XCTAssertEqual(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + } + } + [reloadDataExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; + + // Cause table view to enter editing mode and then scroll to the bottom. + // The last node should be re-measured on main thread with the new (smaller) content view width. + NSIndexPath *lastRowIndexPath = [NSIndexPath indexPathForRow:(NumberOfRowsPerSection - 1) inSection:(NumberOfSections - 1)]; + XCTestExpectation *relayoutExpectation = [self expectationWithDescription:@"relayout"]; + [tableView beginUpdates]; + [tableView setEditing:YES]; + [tableView setContentOffset:CGPointMake(0, CGFLOAT_MAX) animated:YES]; + [tableView endUpdatesAnimated:YES completion:^(BOOL completed) { + ASTestTextCellNode *node = (ASTestTextCellNode *)[tableView nodeForRowAtIndexPath:lastRowIndexPath]; + XCTAssertEqual(node.numberOfLayoutsOnMainThread, 1); + XCTAssertLessThan(node.constrainedSizeForCalculatedLayout.max.width, tableViewSize.width); + [relayoutExpectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + @end From 0c068c442da767b0f2a4996f489717cc5eafa8e7 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 16 Sep 2015 14:17:29 +0300 Subject: [PATCH 53/54] Fix appledoc warnings in ASLayoutSpec.h --- AsyncDisplayKit/Layout/ASLayoutSpec.h | 82 ++++++++++++++++++++------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 1c3c901a..2dd00198 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -14,47 +14,85 @@ @interface ASLayoutSpec : NSObject /** - Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a - layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be - set to NO and any further mutations will cause an assert. + * Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a + * layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be + * set to NO and any further mutations will cause an assert. */ @property (nonatomic, assign) BOOL isMutable; - (instancetype)init; /** - * Set child methods + * Adds a child to this layout spec using a default identifier. * - * Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the - * reponsibility of holding on to the spec children. For a layout spec like ASInsetLayoutSpec that - * only requires a single child, the child can be added by calling setChild:. + * @param child A child to be added. * - * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) - * a subclass should use the setChild to set the "primary" child. It can then use setChild:forIdentifier: - * to set any other required children. Ideally a subclass would hide this from the user, and use the - * setChildWithIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild - * property that behind the scenes is calling setChild:forIdentifier:. + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * reponsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, + * only require a single child. * - * Finally, a layout spec like ASStackLayoutSpec can take an unknown number of children. In this case, - * the setChildren: method should be used. For good measure, in these layout specs it probably makes - * sense to define setChild: to do something appropriate or to assert. + * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) + * a subclass should use this method to set the "primary" child. It can then use setChild:forIdentifier: + * to set any other required children. Ideally a subclass would hide this from the user, and use the + * setChild:forIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild + * property that behind the scenes is calling setChild:forIdentifier:. */ - (void)setChild:(id)child; + +/** + * Adds a child with the given identifier to this layout spec. + * + * @param child A child to be added. + * + * @param identifier An identifier associated with the child. + * + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * reponsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec, + * only require a single child. + * + * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) + * a subclass should use the setChild method to set the "primary" child. It can then use this method + * to set any other required children. Ideally a subclass would hide this from the user, and use the + * setChild:forIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild + * property that behind the scenes is calling setChild:forIdentifier:. + */ - (void)setChild:(id)child forIdentifier:(NSString *)identifier; + +/** + * Adds childen to this layout spec. + * + * @param children An array of ASLayoutable children to be added. + * + * @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the + * reponsibility of holding on to the spec children. Some layout specs, like ASStackLayoutSpec, + * can take an unknown number of children. In this case, the this method should be used. + * For good measure, in these layout specs it probably makes sense to define + * setChild: and setChild:forIdentifier: methods to do something appropriate or to assert. + */ - (void)setChildren:(NSArray *)children; /** - * Get child methods + * Get child methods * - * There is a corresponding "getChild" method for the above "setChild" methods. If a subclass - * has extra layoutable children, it is recommended to make a corresponding get method for that - * child. For example, the ASBackgroundLayoutSpec responds to backgroundChild. - * - * If a get method is called on a spec that doesn't make sense, then the standard is to assert. - * For example, calling children on an ASInsetLayoutSpec will assert. + * There is a corresponding "getChild" method for the above "setChild" methods. If a subclass + * has extra layoutable children, it is recommended to make a corresponding get method for that + * child. For example, the ASBackgroundLayoutSpec responds to backgroundChild. + * + * If a get method is called on a spec that doesn't make sense, then the standard is to assert. + * For example, calling children on an ASInsetLayoutSpec will assert. */ + +/** Returns the child added to this layout spec using the default identifier. */ - (id)child; + +/** + * Returns the child added to this layout spec using the given identifier. + * + * @param identifier An identifier associated withe the child. + */ - (id)childForIdentifier:(NSString *)identifier; + +/** Returns all children added to this layout spec. */ - (NSArray *)children; @end From 094d2570f5150356fdea1cfee57ad0cd2dba5ee0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 16 Sep 2015 15:19:55 +0300 Subject: [PATCH 54/54] Force the layout of subviews when _ASTableViewCell transitioned to another state Relayout will be triggered in layoutSubviews (if needed). --- AsyncDisplayKit/ASTableView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 530b7687..b7ebe9c2 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -128,6 +128,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)didTransitionToState:(UITableViewCellStateMask)state { [self setNeedsLayout]; + [self layoutIfNeeded]; [super didTransitionToState:state]; }