From 15b3fd6eab3dccc4c8fb1502cef8b740237acc2a Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 09:36:22 -0700 Subject: [PATCH] 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 cadfc1ed66..441430497b 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 974bbe1272..7fcfbd5999 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,7 +13,7 @@ #import #import -#import +#import /** * UIView creation block. Used to create the backing view of a new display node. @@ -40,7 +40,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 13ee5fdf00..4b5419cc40 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -41,12 +41,6 @@ @implementation ASDisplayNode -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) @@ -155,7 +149,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } _methodOverrides = overrides; - _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; } diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 1b35441c81..e8d5b76154 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,7 +7,6 @@ */ #import -#import @protocol ASTextNodeDelegate; @@ -30,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @abstract Draws interactive rich text. @discussion Backed by TextKit. */ -@interface ASTextNode : ASControlNode +@interface ASTextNode : ASControlNode /** @abstract The attributed string to show. diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 7ca68f5177..68f4f1fdab 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,7 +17,6 @@ #import #import -#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -108,9 +107,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) UILongPressGestureRecognizer *_longPressGestureRecognizer; } -@synthesize ascender = _ascender; -@synthesize descender = _descender; - #pragma mark - NSObject - (instancetype)init @@ -359,9 +355,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); - - _ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - _descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 46587d996b..d147c76700 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -9,7 +9,7 @@ */ #import -#import +#import typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ @@ -29,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { If the spec is created with a vertical direction, a child's vertical spacing will be measured from its baseline instead of from the child's bounding box. */ -@interface ASBaselineLayoutSpec : ASLayoutSpec +@interface ASBaselineLayoutSpec : ASLayoutSpec /** Specifies the direction children are stacked in. */ @property (nonatomic, assign) ASStackLayoutDirection direction; diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 45fa687f8c..50139a548b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -9,7 +9,6 @@ */ #import "ASBaselineLayoutSpec.h" -#import "ASStackLayoutable.h" #import #import @@ -26,12 +25,6 @@ @implementation ASBaselineLayoutSpec -{ - ASDN::RecursiveMutex _propertyLock; -} - -@synthesize ascender = _ascender; -@synthesize descender = _descender; - (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { @@ -61,8 +54,8 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } @@ -74,25 +67,11 @@ NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; - ASDN::MutexLocker l(_propertyLock); - _ascender = baselinePositionedLayout.ascender; - _descender = baselinePositionedLayout.descender; - return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; -#if DEBUG - for (id child in children) { - NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h index 5e05029964..7e52b2c056 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h @@ -6,9 +6,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "ASStackLayoutable.h" +#import -@protocol ASBaselineLayoutable +@protocol ASBaselineLayoutable /** * @abstract The distance from the top of the layoutable object to its baseline diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h new file mode 100644 index 0000000000..0bdee1ad09 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -0,0 +1,49 @@ +// +// ASLayoutOptions.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@protocol ASLayoutable; + +#import +#import +#import + +@interface ASLayoutOptions : NSObject + +- (instancetype)initWithLayoutable:(id)layoutable; +- (void)setValuesFromLayoutable:(id)layoutable; + +@property (nonatomic, assign) BOOL isMutable; + +#if DEBUG +@property (nonatomic, assign) NSUInteger changeMonitor; +#endif + +#pragma mark - ASStackLayoutable + +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +#pragma mark - ASBaselineLayoutable + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +#pragma mark - ASStaticLayoutable + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint position; + +- (void)setupDefaults; + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m new file mode 100644 index 0000000000..6207ef0d07 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -0,0 +1,120 @@ +// +// ASLayoutOptions.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptions.h" + +#import +#import +#import "ASInternalHelpers.h" +#import + +@implementation ASLayoutOptions + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; +#if DEBUG + [self addObserver:self forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; +#endif + } + return self; +} + +#if DEBUG ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor +{ + NSMutableSet *keys = [NSMutableSet set]; + unsigned int count; + + objc_property_t *properties = class_copyPropertyList([self class], &count); + for (size_t i = 0; i < count; ++i) { + NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; + + if ([property isEqualToString: @"observableSelf"] == NO) { + [keys addObject: property]; + } + } + free(properties); + + return keys; +} + +#endif + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ +#if DEBUG + if ([keyPath isEqualToString:@"changeMonitor"]) { + ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); + } else +#endif + { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.position = self.position; + + return copy; +} + +#pragma mark - Defaults +- (void)setupDefaults +{ + _flexBasis = ASRelativeDimensionUnconstrained; + _spacingBefore = 0; + _spacingAfter = 0; + _flexGrow = NO; + _flexShrink = NO; + _alignSelf = ASStackLayoutAlignSelfAuto; + + _ascender = 0; + _descender = 0; + + _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + _position = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.position = displayNode.frame.origin; + } +} + + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index f599c84813..dc4f100bb5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,10 +8,10 @@ * */ -#import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -23,12 +23,15 @@ - (instancetype)init; - (void)setChild:(id)child; -- (id)child; - - (void)setChild:(id)child forIdentifier:(NSString *)identifier; -- (id)childForIdentifier:(NSString *)identifier; - - (void)setChildren:(NSArray *)children; + +- (id)child; +- (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child; ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 58ecd114e9..f277a01bc4 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import + static NSString * const kDefaultChildKey = @"kDefaultChildKey"; static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @@ -25,12 +27,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -39,7 +35,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return nil; } _layoutChildren = [NSMutableDictionary dictionary]; - _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; } @@ -61,15 +56,34 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } -- (id)child -{ - return self.layoutChildren[kDefaultChildKey]; -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - self.layoutChildren[identifier] = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + layoutOptions.isMutable = NO; + self.layoutChildren[identifier] = child; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + } + + [finalChildren addObject:finalLayoutable]; + } + + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; } - (id)childForIdentifier:(NSString *)identifier @@ -77,14 +91,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[identifier]; } -- (void)setChildren:(NSArray *)children +- (id)child { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; - for (id child in children) { - [finalChildren addObject:[child finalLayoutable]]; - } - self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; + return self.layoutChildren[kDefaultChildKey]; } - (NSArray *)children @@ -92,4 +101,35 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } +static Class gLayoutOptionsClass = [ASLayoutOptions class]; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass +{ + gLayoutOptionsClass = layoutOptionsClass; +} + ++ (ASLayoutOptions *)optionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; + [layoutOptions setValuesFromLayoutable:child]; + layoutOptions.isMutable = NO; + return layoutOptions; +} + ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child +{ + objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); + if (layoutOptions == nil) { + layoutOptions = [self optionsForChild:child]; + [self associateLayoutOptions:layoutOptions withChild:child]; + } + return objc_getAssociatedObject(child, @selector(setChild:)); +} + + + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ff1ee030a..8ba86fdc1b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -10,6 +10,7 @@ #import #import +#import @class ASLayout; @class ASLayoutSpec; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 07243f30cc..ef109d47ae 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -72,17 +72,6 @@ _spacing = spacing; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; - -#if DEBUG - for (id child in children) { - ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); @@ -91,9 +80,8 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { - NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index b23b4fbbdb..e26d0a0210 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -8,9 +8,10 @@ * */ -#import +#import +#import -@protocol ASStackLayoutable +@protocol ASStackLayoutable /** * @abstract Additional space to place before this object in the stacking direction. diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 585bf94660..b458207b8e 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 5618fa6e9b..8e7d74acf3 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -8,10 +8,9 @@ * */ -#import #import -@protocol ASStaticLayoutable +@protocol ASStaticLayoutable /** If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index 2e3d2b3bfe..bf0239c952 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -15,15 +15,14 @@ static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - - __weak id child = (id) layout.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return child.ascender; + return layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + child.descender; + return layout.size.height + layoutOptions.descender; } } @@ -34,10 +33,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id child = (id)l.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - child.ascender; + return maxAscender - layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -91,9 +90,11 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; + ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; + ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; + return layoutOptionsA.ascender < layoutOptionsB.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -106,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id child = (id) l.layoutableObject; - p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -124,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = child.descender; + spacingAfterBaseline = layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -151,12 +152,12 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi /* Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements - ASBaselineLayoutable and needs an ascender and descender to lay itself out properly. + ASLayoutable and needs an ascender and descender to lay itself out properly. */ const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id)(*descenderIt).layoutableObject).descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 84bbdac502..31ff00b595 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -19,7 +19,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - switch (alignment(l.child.alignSelf, style.alignItems)) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + switch (alignment(layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -48,14 +49,15 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - p = p + directionPoint(style.direction, l.child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 93a2efcb20..4112af8e66 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -16,7 +16,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout { const CGFloat violation; /** Given a set of children, computes the unpositioned layouts for those children. */ - static ASStackUnpositionedLayout compute(const std::vector> &children, + static ASStackUnpositionedLayout compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange); }; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index cc3be53d1c..da4000cd5e 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,14 +18,15 @@ /** Sizes the child given the parameters specified, and returns the computed layout. */ -static ASLayout *crossChildLayout(const id child, +static ASLayout *crossChildLayout(const id child, const ASStackLayoutSpecStyle style, const CGFloat stackMin, const CGFloat stackMax, const CGFloat crossMin, const CGFloat crossMax) { - const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); // stretched children will have a cross dimension of at least crossMin const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); @@ -75,7 +76,8 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -111,7 +113,8 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - return child.flexGrow && child.flexShrink; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + return layoutOptions.flexGrow && layoutOptions.flexShrink; } /** If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific number then we may avoid the first "intrinsic" size calculation. */ -ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, +ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { @@ -283,7 +287,7 @@ static void flexChildrenAlongStackDimension(std::vector Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and stretched. */ -static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, +static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange, const CGSize size, @@ -292,9 +296,10 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); - return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; @@ -312,7 +317,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac }); } -ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, +ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 5a7cbd0a92..6ea803ba93 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -94,7 +94,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - foregroundNode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 9b6c3d655d..2e9c2e7802 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -41,8 +41,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - subnode.flexGrow = flex; - subnode.flexShrink = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; } return subnodes; } @@ -114,7 +114,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - ((ASDisplayNode *)subnodes[1]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; // Width is 75px--that's less than the sum of the widths of the children, which is 100px. static ASSizeRange kSize = {{75, 0}, {75, 150}}; @@ -204,23 +204,25 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; + layoutOptions1.spacingBefore = 10; + layoutOptions2.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0; + layoutOptions1.spacingBefore = 0; + layoutOptions2.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20; + layoutOptions1.spacingAfter = 10; + layoutOptions2.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0; + layoutOptions1.spacingAfter = 0; + layoutOptions2.spacingAfter = 0; style.spacing = 10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10; + layoutOptions1.spacingBefore = -10; + layoutOptions2.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -236,9 +238,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -254,9 +256,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - child1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - child1.flexGrow = YES; - child1.flexShrink = YES; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; + layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); + layoutOptions1.flexGrow = YES; + layoutOptions1.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -271,11 +274,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - subnode1.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -291,7 +294,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.alignSelf = ASStackLayoutAlignSelfCenter; + [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -311,9 +314,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -332,9 +335,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -353,9 +356,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -374,9 +377,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -396,9 +399,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -419,8 +422,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; + layoutOptions.flexGrow = YES; + layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -439,12 +443,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - ((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -460,7 +464,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20); + [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -478,8 +482,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - child2.flexGrow = YES; - child2.flexShrink = YES; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; + layoutOptions2.flexGrow = YES; + layoutOptions2.flexShrink = YES; // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. @@ -504,13 +509,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - ((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - ((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - ((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES; // A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, // causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px. diff --git a/Podfile.lock b/Podfile.lock index 423c2d2851..119b17c410 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.37.2 +COCOAPODS: 0.35.0 diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 293b513d1e..1e08bbe8c1 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -58,7 +58,9 @@ 1A943BF0259746F18D6E423F /* Frameworks */, 1AE410B73DA5C3BD087ACDD7 /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 05E2128219D4DB510098F589 /* Products */ = { isa = PBXGroup;