From 95016021b47ec5a0fc18ebad77a0621bcfabe586 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Thu, 9 Jul 2015 16:09:20 +0200 Subject: [PATCH 001/145] Access to underlying UITextView for more configuration options. --- AsyncDisplayKit/ASEditableTextNode.h | 3 +++ AsyncDisplayKit/ASEditableTextNode.mm | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index 92811e2457..903fbbee43 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -19,6 +19,9 @@ #pragma mark - Configuration +//! @abstract Access to underlying UITextView for more configuration options. +@property (nonatomic, readwrite, strong) UITextView *textView; + //! @abstract The attributes to apply to new text being entered by the user. @property (nonatomic, readwrite, strong) NSDictionary *typingAttributes; diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 3761134481..84e3cc1572 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -77,6 +77,7 @@ _textKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; _textKitComponents.layoutManager.delegate = self; _wordKerner = [[ASTextNodeWordKerner alloc] init]; + _textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; // Create the placeholder scaffolding. _placeholderTextKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; @@ -133,7 +134,7 @@ [self.view addSubview:_placeholderTextKitComponents.textView]; // Create and configure our text view. - _textKitComponents.textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; + _textKitComponents.textView = _textView; //_textKitComponents.textView = NO; // Unfortunately there's a bug here with iOS 7 DP5 that causes the text-view to only be one line high when scrollEnabled is NO. rdar://14729288 _textKitComponents.textView.delegate = self; _textKitComponents.textView.editable = YES; From cfac6c992ac95593654b31a0f6573ab7bfcab24b Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 10 Jul 2015 19:02:04 +0700 Subject: [PATCH 002/145] [appledoc] Fix appledoc warnings. (#553) --- AsyncDisplayKit/ASCollectionView.h | 22 +++++++++---- AsyncDisplayKit/ASControlNode.h | 9 +++++- AsyncDisplayKit/ASDisplayNode+Subclasses.h | 4 +++ AsyncDisplayKit/ASDisplayNode.h | 22 ++++++++++--- AsyncDisplayKit/ASEditableTextNode.h | 9 ++++-- AsyncDisplayKit/ASMultiplexImageNode.h | 8 +++++ AsyncDisplayKit/ASNetworkImageNode.h | 6 ++++ AsyncDisplayKit/ASTableView.h | 9 +++++- AsyncDisplayKit/Details/ASDataController.h | 2 +- AsyncDisplayKit/Details/ASRangeController.h | 36 +++++++++++++++++++++ 10 files changed, 111 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index ec6fd3242e..8254a95b64 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -31,11 +31,11 @@ @property (nonatomic, weak) id asyncDelegate; // must not be nil /** - * Tuning parameters for a range. + * Tuning parameters for a range type. * - * @param range The range to get the tuning parameters for. + * @param rangeType The range type to get the tuning parameters for. * - * @returns A tuning parameter value for the given range. + * @returns A tuning parameter value for the given range type. * * Defaults to the render range having one sceenful both leading and trailing and the preload range having two * screenfuls in both directions. @@ -43,16 +43,24 @@ - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; /** - * Set the tuning parameters for a range. + * Set the tuning parameters for a range type. * - * @param tuningParameters The tuning parameters to store for a range. - * @param range The range to set the tuning parameters for. + * @param tuningParameters The tuning parameters to store for a range type. + * @param rangeType The range type to set the tuning parameters for. */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; /** * Initializer. * + * @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview + * in which you plan to add it. This frame is passed to the superclass during initialization. + * + * @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. + * Must not be nil. + * + * @param asyncDataFetchingEnabled Enable the data fetching in async mode. + * * @discussion If asyncDataFetching is enabled, the `AScollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and * `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically * from calling thread. @@ -156,7 +164,7 @@ /** * Similar to -collectionView:cellForItemAtIndexPath:. * - * @param collection The sender. + * @param collectionView The sender. * * @param indexPath The index path of the requested node. * diff --git a/AsyncDisplayKit/ASControlNode.h b/AsyncDisplayKit/ASControlNode.h index b94bb77227..d08a09c69a 100644 --- a/AsyncDisplayKit/ASControlNode.h +++ b/AsyncDisplayKit/ASControlNode.h @@ -15,14 +15,21 @@ */ typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent) { + /** A touch-down event in the control node. */ ASControlNodeEventTouchDown = 1 << 0, + /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */ ASControlNodeEventTouchDownRepeat = 1 << 1, + /** An event where a finger is dragged inside the bounds of the control node. */ ASControlNodeEventTouchDragInside = 1 << 2, + /** An event where a finger is dragged just outside the bounds of the control. */ ASControlNodeEventTouchDragOutside = 1 << 3, + /** A touch-up event in the control node where the finger is inside the bounds of the node. */ ASControlNodeEventTouchUpInside = 1 << 4, + /** A touch-up event in the control node where the finger is outside the bounds of the node. */ ASControlNodeEventTouchUpOutside = 1 << 5, + /** A system event canceling the current touches for the control node. */ ASControlNodeEventTouchCancel = 1 << 6, - + /** All events, including system events. */ ASControlNodeEventAllEvents = 0xFFFFFFFF }; diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index fec0527f27..745b0c3240 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -267,6 +267,8 @@ * @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no * subnodes present. * + * @param subnode The subnode of which display is about to begin. + * * @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) is * about to begin. */ @@ -276,6 +278,8 @@ * @abstract Indicates that the receiver is finished displaying its subnodes. This method is not called if there are * no subnodes present. * + * @param subnode The subnode of which display is about to completed. + * * @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) has * completed. */ diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index bd0c5727ab..efa17d524d 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,7 +13,13 @@ #import #import +/** + * UIView creation block. Used to create the backing view of a new display node. + */ typedef UIView *(^ASDisplayNodeViewBlock)(); +/** + * CALayer creation block. Used to create the backing layer of a new display node. + */ typedef CALayer *(^ASDisplayNodeLayerBlock)(); /** @@ -50,6 +56,8 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); /** * @abstract Alternative initializer with a block to create the backing view. * + * @param viewBlock The block that will be used to create the backing view. + * * @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. */ @@ -58,6 +66,8 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); /** * @abstract Alternative initializer with a block to create the backing layer. * + * @param viewBlock 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. */ @@ -332,7 +342,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * This method is used to notify the node that it should purge any content that is both expensive to fetch and to * retain in memory. * - * @see clearFetchedData and fetchData + * @see ASDisplayNode(Subclassing):clearFetchedData and fetchData */ - (void)recursivelyClearFetchedData; @@ -341,7 +351,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * * @discussion Fetches content from remote sources for the current node and all subnodes. * - * @see fetchData and clearFetchedData + * @see ASDisplayNode(Subclassing):fetchData and clearFetchedData */ - (void)recursivelyFetchData; @@ -556,18 +566,22 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); @interface ASDisplayNode (ASDisplayNodeAsyncTransactionContainer) @end - +/** UIVIew(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to an UIView. */ @interface UIView (AsyncDisplayKit) /** * Convenience method, equivalent to [view addSubview:node.view] or [view.layer addSublayer:node.layer] if layer-backed. + * + * @param node The node to be added. */ - (void)addSubnode:(ASDisplayNode *)node; @end - +/** CALayer(AsyncDisplayKit) defines convenience method for adding sub-ASDisplayNode to a CALayer. */ @interface CALayer (AsyncDisplayKit) /** * Convenience method, equivalent to [layer addSublayer:node.layer]. + * + * @param node The node to be added. */ - (void)addSubnode:(ASDisplayNode *)node; @end diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index 92811e2457..fb3354c8e8 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -11,10 +11,10 @@ @protocol ASEditableTextNodeDelegate; -//! @abstract ASEditableTextNode implements a node that supports text editing. +/// @abstract ASEditableTextNode implements a node that supports text editing. @interface ASEditableTextNode : ASDisplayNode -//! @abstract The text node's delegate, which must conform to the protocol. +// @abstract The text node's delegate, which must conform to the protocol. @property (nonatomic, readwrite, weak) id delegate; #pragma mark - Configuration @@ -74,6 +74,11 @@ @end #pragma mark - +/** + * The methods declared by the ASEditableTextNodeDelegate protocol allow the adopting delegate to + * respond to notifications such as began and finished editing, selection changed and text updated; + * and manage whether a specified text should be replaced. + */ @protocol ASEditableTextNodeDelegate @optional diff --git a/AsyncDisplayKit/ASMultiplexImageNode.h b/AsyncDisplayKit/ASMultiplexImageNode.h index c62f7c3153..db10daa324 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.h +++ b/AsyncDisplayKit/ASMultiplexImageNode.h @@ -102,6 +102,10 @@ typedef NS_ENUM(NSUInteger, ASMultiplexImageNodeErrorCode) { #pragma mark - +/** + * The methods declared by the ASMultiplexImageNodeDelegate protocol allow the adopting delegate to respond to + * notifications such as began, progressed and finished downloading, updated and displayed an image. + */ @protocol ASMultiplexImageNodeDelegate @optional @@ -170,6 +174,10 @@ didFinishDownloadingImageWithIdentifier:(id)imageIdentifier #pragma mark - +/** + * The ASMultiplexImageNodeDataSource protocol is adopted by an object that provides the multiplex image node, + * for each image identifier, an image or a URL the image node should load. + */ @protocol ASMultiplexImageNodeDataSource @optional diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 7039004c95..5dc723521e 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -61,6 +61,8 @@ /** * Download and display a new image. * + * @param URL The URL of a new image to download and display. + * * @param reset Whether to display a placeholder () while loading the new image. */ - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset; @@ -74,6 +76,10 @@ #pragma mark - +/** + * The methods declared by the ASNetworkImageNodeDelegate protocol allow the adopting delegate to respond to + * notifications such as fininished decoding and downloading an image. + */ @protocol ASNetworkImageNodeDelegate /** diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 8b399a01b4..1e0534c83b 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -51,7 +51,14 @@ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType; /** - * initializer. + * Initializer. + * + * @param frame A rectangle specifying the initial location and size of the table view in its superview’€™s coordinates. + * The frame of the table view changes as table cells are added and deleted. + * + * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. + * + * @param asyncDataFetchingEnabled Enable the data fetching in async mode. * * @discussion If asyncDataFetching is enabled, the `ASTableView` will fetch data through `tableView:numberOfRowsInSection:` and * `tableView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 08dfd4fbf2..5ec49b5ce0 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -111,7 +111,7 @@ typedef NSUInteger ASDataControllerAnimationOptions; @property (nonatomic, weak) id delegate; /** - * Designated iniailizer. + * Designated initializer. * * @param asyncDataFetchingEnabled Enable the data fetching in async mode. * diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index cdfb0568b4..d8dd1bf58e 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -30,6 +30,8 @@ * Notify the range controller that the visible range has been updated. * This is the primary input call that drives updating the working ranges, and triggering their actions. * + * @param scrollDirection The current scroll direction of the scroll view. + * * @see [ASRangeControllerDelegate rangeControllerVisibleNodeIndexPaths:] */ - (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection; @@ -74,36 +76,70 @@ /** * Begin updates. + * + * @param rangeController Sender. */ - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController; /** * End updates. + * + * @param rangeController Sender. + * + * @param completion Completion block. */ - (void)rangeControllerEndUpdates:(ASRangeController * )rangeController completion:(void (^)(BOOL))completion ; /** * Fetch nodes at specific index paths. + * + * @param rangeController Sender. + * + * @param indexPaths Index paths. */ - (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths; /** * Called for nodes insertion. + * + * @param rangeController Sender. + * + * @param indexPaths Index path of inserted nodes. + * + * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ - (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** * Called for nodes deletion. + * + * @param rangeController Sender. + * + * @param indexPaths Index path of deleted nodes. + * + * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ - (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** * Called for section insertion. + * + * @param rangeController Sender. + * + * @param indexSet Index set of inserted sections. + * + * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** * Called for section deletion. + * + * @param rangeController Sender. + * + * @param indexSet Index set of deleted sections. + * + * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; From e1fd58268c8643770ffd047a80bf1264a4ab6e33 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 10 Jul 2015 11:28:43 +0700 Subject: [PATCH 003/145] Clean up #468: - -layoutSpecThatFits: must return an ASLayoutSpec. - Move ASDisplayNode's -measureWithSizeRange: redeclaration to ASDisplayNode.h. - Rename ASStackLayoutChild.h to ASStackLayoutDefines.h. - Rename ASStaticLayoutSpecDimension.h to ASRelativeSize.h. - Don't import ASLayout.h in other headers to prevent circular inclusions. - Explain use cases of ASLayout's initializers. - Clean up ASInternalHelpers.h. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 24 +++++++++---------- AsyncDisplayKit/ASCellNode.m | 2 +- AsyncDisplayKit/ASDisplayNode+Subclasses.h | 24 +++---------------- AsyncDisplayKit/ASDisplayNode.h | 21 +++++++++++++++- AsyncDisplayKit/ASDisplayNode.mm | 6 +++-- AsyncDisplayKit/AsyncDisplayKit.h | 2 +- .../Layout/ASBackgroundLayoutSpec.mm | 1 + AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 1 + AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 1 + AsyncDisplayKit/Layout/ASLayout.h | 9 +++++-- AsyncDisplayKit/Layout/ASLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASLayoutable.h | 2 +- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 1 + AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 1 + ...LayoutSpecDimension.h => ASRelativeSize.h} | 0 ...youtSpecDimension.mm => ASRelativeSize.mm} | 2 +- ...ckLayoutChild.h => ASStackLayoutDefines.h} | 0 AsyncDisplayKit/Layout/ASStaticLayoutSpec.h | 2 +- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 1 + .../Private/ASDisplayNodeInternal.h | 1 - AsyncDisplayKit/Private/ASInternalHelpers.h | 4 +--- .../ASLayoutSpecSnapshotTestsHelper.m | 1 + 22 files changed, 59 insertions(+), 48 deletions(-) rename AsyncDisplayKit/Layout/{ASStaticLayoutSpecDimension.h => ASRelativeSize.h} (100%) rename AsyncDisplayKit/Layout/{ASStaticLayoutSpecDimension.mm => ASRelativeSize.mm} (98%) rename AsyncDisplayKit/Layout/{ASStackLayoutChild.h => ASStackLayoutDefines.h} (100%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 7404eefa77..3cab5c824f 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -187,13 +187,13 @@ 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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; - AC21EC101B3D0BF600C8B19A /* ASStackLayoutChild.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 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, ); }; }; AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; }; AC3C4A541A113EEC00143C57 /* ASCollectionViewProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC47D9421B3B891B00AAEE9D /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; }; - AC47D9451B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */; settings = {ATTRIBUTES = (Public, ); }; }; - AC47D9461B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */; }; + AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; + AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; }; AC6456091B0A335000CF11B8 /* ASCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.m */; }; ACF6ED1A1B17843500DA7C62 /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; }; @@ -526,12 +526,12 @@ 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 = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; - AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutChild.h; path = AsyncDisplayKit/Layout/ASStackLayoutChild.h; 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 = ""; }; AC3C4A501A1139C100143C57 /* ASCollectionView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionView.mm; sourceTree = ""; }; AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewProtocols.h; sourceTree = ""; }; - AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutSpecDimension.h; path = AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h; sourceTree = ""; }; - AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStaticLayoutSpecDimension.mm; path = AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm; sourceTree = ""; }; + AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeSize.h; path = AsyncDisplayKit/Layout/ASRelativeSize.h; sourceTree = ""; }; + AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeSize.mm; path = AsyncDisplayKit/Layout/ASRelativeSize.mm; sourceTree = ""; }; AC6456071B0A335000CF11B8 /* ASCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCellNode.m; sourceTree = ""; }; ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = ""; }; ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASBackgroundLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm; sourceTree = ""; }; @@ -914,6 +914,8 @@ ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, ACF6ED081B17843500DA7C62 /* ASDimension.mm */, + AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */, + AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */, ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */, ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */, ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, @@ -925,13 +927,11 @@ ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */, ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */, ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */, - AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutChild.h */, + AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, - AC47D9431B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h */, - AC47D9441B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm */, ); name = Layout; path = ..; @@ -971,8 +971,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - AC21EC101B3D0BF600C8B19A /* ASStackLayoutChild.h in Headers */, - AC47D9451B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.h in Headers */, + AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */, + AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */, ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */, ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, @@ -1374,7 +1374,7 @@ ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */, 055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */, 205F0E121B371BD7007741D0 /* ASScrollDirection.m in Sources */, - AC47D9461B3BB41900AAEE9D /* ASStaticLayoutSpecDimension.mm in Sources */, + AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */, ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */, ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */, 464052261A3F83C40061C0BA /* ASMultidimensionalArrayUtils.mm in Sources */, diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 39aadab6bb..4273a10cc1 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -106,7 +106,7 @@ static const CGFloat kFontSize = 18.0f; return self; } -- (id)layoutSpecThatFits:(ASSizeRange)constrainedSize +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { static const CGFloat kHorizontalPadding = 15.0f; static const CGFloat kVerticalPadding = 11.0f; diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index fec0527f27..34cb3dfae0 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -13,8 +13,7 @@ #import #import -#import -#import +@class ASLayoutSpec; /** * The subclass header _ASDisplayNode+Subclasses_ defines the following methods that either must or can be overriden by @@ -36,7 +35,7 @@ * variables. */ -@interface ASDisplayNode (Subclassing) +@interface ASDisplayNode (Subclassing) /** @name View Configuration */ @@ -120,23 +119,6 @@ /** @name Layout calculation */ -/** - * @abstract Asks the node to measure a layout based on given size range. - * - * @param constrainedSize The minimum and maximum sizes the receiver should fit in. - * - * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). - * - * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the - * constraint and the result. - * - * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may - * be expensive if result is not cached. - * - * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] - */ -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; - /** * @abstract Calculate a layout based on given size range. * @@ -180,7 +162,7 @@ * * @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead. */ -- (id)layoutSpecThatFits:(ASSizeRange)constrainedSize; +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize; /** * @abstract Invalidate previously measured and cached layout. diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index bd0c5727ab..4fbd32812c 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,6 +13,8 @@ #import #import +#import + typedef UIView *(^ASDisplayNodeViewBlock)(); typedef CALayer *(^ASDisplayNodeLayerBlock)(); @@ -32,7 +34,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ @@ -137,6 +139,23 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ - (CGSize)measure:(CGSize)constrainedSize; +/** + * @abstract Asks the node to measure a layout based on given size range. + * + * @param constrainedSize The minimum and maximum sizes the receiver should fit in. + * + * @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used). + * + * @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the + * constraint and the result. + * + * @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may + * be expensive if result is not cached. + * + * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] + */ +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; + /** * @abstract Return the calculated size. * diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index bf81cdc960..43dbc037fa 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -19,6 +19,8 @@ #import "ASDisplayNodeExtras.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" +#import "ASLayoutSpec.h" @interface ASDisplayNode () @@ -1307,10 +1309,10 @@ static NSInteger incrementIfFound(NSInteger i) { return CGSizeZero; } -- (id)layoutSpecThatFits:(ASSizeRange)constrainedSize +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - return nil; + return [ASLayoutSpec new]; } - (ASLayout *)calculatedLayout diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/AsyncDisplayKit/AsyncDisplayKit.h index 46b3677f1b..b65d144f69 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/AsyncDisplayKit/AsyncDisplayKit.h @@ -35,5 +35,5 @@ #import #import #import -#import +#import #import diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index 135fc372f4..ae03e78836 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -12,6 +12,7 @@ #import "ASAssert.h" #import "ASBaseDefines.h" +#import "ASLayout.h" @interface ASBackgroundLayoutSpec () { diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index 0ef0f4848d..f96b359172 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -11,6 +11,7 @@ #import "ASCenterLayoutSpec.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" @implementation ASCenterLayoutSpec { diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index c5f3043107..8db3975773 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -14,6 +14,7 @@ #import "ASBaseDefines.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" @interface ASInsetLayoutSpec () { diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 5bdd2190c8..3cc5158840 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -38,14 +38,19 @@ extern BOOL CGPointIsNull(CGPoint point); sublayouts:(NSArray *)sublayouts; /** - * Convenience that has CGPointNull position. + * Convenience that has CGPointNull position. + * Best used by ASDisplayNode subclasses that are manually creating a layout for -calculateLayoutThatFits:, + * or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree, + * or for creating a sublayout of which the position is yet to be determined. */ + (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size sublayouts:(NSArray *)sublayouts; /** - * Convenience that has CGPointNull position and no sublayouts. + * Convenience that has CGPointNull position and no sublayouts. + * Best used for creating a layout that has no sublayouts, and is either a root one + * or a sublayout of which the position is yet to be determined. */ + (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 122033d091..c2602824e7 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -9,7 +9,6 @@ */ #import -#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ @interface ASLayoutSpec : NSObject diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 5fc091b12f..ea0acedabb 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -9,7 +9,7 @@ */ #import -#import +#import @class ASLayout; diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 074753f143..b478f69971 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -12,6 +12,7 @@ #import "ASAssert.h" #import "ASBaseDefines.h" +#import "ASLayout.h" @implementation ASOverlayLayoutSpec { diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 103be815a7..2e358b533f 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -17,6 +17,7 @@ #import "ASBaseDefines.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" @implementation ASRatioLayoutSpec { diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h b/AsyncDisplayKit/Layout/ASRelativeSize.h similarity index 100% rename from AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.h rename to AsyncDisplayKit/Layout/ASRelativeSize.h diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm b/AsyncDisplayKit/Layout/ASRelativeSize.mm similarity index 98% rename from AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm rename to AsyncDisplayKit/Layout/ASRelativeSize.mm index 4790cd1582..0410bb5a3c 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpecDimension.mm +++ b/AsyncDisplayKit/Layout/ASRelativeSize.mm @@ -8,7 +8,7 @@ * */ -#import "ASStaticLayoutSpecDimension.h" +#import "ASRelativeSize.h" #import "ASAssert.h" ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {}; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutChild.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h similarity index 100% rename from AsyncDisplayKit/Layout/ASStackLayoutChild.h rename to AsyncDisplayKit/Layout/ASStackLayoutDefines.h diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 91497c2da2..09815c648b 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -9,7 +9,7 @@ */ #import -#import +#import @interface ASStaticLayoutSpecChild : NSObject diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index f7488a7133..daf0971e56 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -12,6 +12,7 @@ #import "ASLayoutSpecUtilities.h" #import "ASInternalHelpers.h" +#import "ASLayout.h" @implementation ASStaticLayoutSpecChild diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index cded26c2c6..39ce70105b 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -17,7 +17,6 @@ #import "ASDisplayNode.h" #import "ASSentinel.h" #import "ASThread.h" -#import "ASLayout.h" BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 4349eb011b..c2549b26c0 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -8,11 +8,9 @@ * */ -#import +#include #import "ASBaseDefines.h" -@class ASLayoutChild; - ASDISPLAYNODE_EXTERN_C_BEGIN BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector); diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m index c15b28f6d4..0141d225e6 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m @@ -12,6 +12,7 @@ #import "ASDisplayNode.h" #import "ASLayoutSpec.h" +#import "ASLayout.h" @interface ASTestNode : ASDisplayNode - (void)setLayoutSpecUnderTest:(ASLayoutSpec *)layoutSpecUnderTest sizeRange:(ASSizeRange)sizeRange; From bbadc9a1e9a7a4b9148891eae6ea274b83320c4d Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 10 Jul 2015 19:46:32 +0700 Subject: [PATCH 004/145] Update nodes in Kittens example to use new signature of layoutSpecThatFits:.. --- examples/Kittens/Sample/BlurbNode.m | 2 +- examples/Kittens/Sample/KittenNode.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Kittens/Sample/BlurbNode.m b/examples/Kittens/Sample/BlurbNode.m index c26cecacad..1c646a25ad 100644 --- a/examples/Kittens/Sample/BlurbNode.m +++ b/examples/Kittens/Sample/BlurbNode.m @@ -74,7 +74,7 @@ static NSString *kLinkAttributeName = @"PlaceKittenNodeLinkAttributeName"; } #if UseAutomaticLayout -- (id)layoutSpecThatFits:(ASSizeRange)constrainedSize +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { return [ASInsetLayoutSpec diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index db435d40ef..7e321712cc 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -129,7 +129,7 @@ static const CGFloat kInnerPadding = 10.0f; } #if UseAutomaticLayout -- (id)layoutSpecThatFits:(ASSizeRange)constrainedSize +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode]; imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize); From 4383e9ab2bf03ad100ddd17507a86e37217a2815 Mon Sep 17 00:00:00 2001 From: Ryan Fitzgerald Date: Thu, 9 Jul 2015 12:31:32 -0400 Subject: [PATCH 005/145] releasing downloaded image when multiplex image node exits render range --- AsyncDisplayKit/ASMultiplexImageNode.mm | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/AsyncDisplayKit/ASMultiplexImageNode.mm index 0bc9c6e8f8..d32d3be061 100644 --- a/AsyncDisplayKit/ASMultiplexImageNode.mm +++ b/AsyncDisplayKit/ASMultiplexImageNode.mm @@ -139,6 +139,13 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent */ - (void)_downloadImageWithIdentifier:(id)imageIdentifier URL:(NSURL *)imageURL completion:(void (^)(UIImage *image, NSError *error))completionBlock; +/** + @abstract Returns a Boolean value indicating whether the downloaded image should be removed when clearing fetched data + @discussion Downloaded image data should only be cleared out if a cache is present + @return YES if an image cache is available; otherwise, NO. + */ +- (BOOL)_shouldClearFetchedImageData; + @end @implementation ASMultiplexImageNode @@ -173,11 +180,15 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent } } -- (void)displayWillStart +- (void)clearFetchedData { - [super displayWillStart]; - - [self fetchData]; + [super clearFetchedData]; + + if ([self _shouldClearFetchedImageData]) { + // setting this to nil makes the node fetch images the next time its display starts + _loadedImageIdentifier = nil; + self.image = nil; + } } - (void)fetchData @@ -626,4 +637,8 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent [self _loadNextImage]; } +- (BOOL)_shouldClearFetchedImageData { + return _cache != nil; +} + @end From 3992e9b09b2f668f2aff79a3e3ebaca91ae6be38 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 12 Jul 2015 12:33:37 +0700 Subject: [PATCH 006/145] [appledoc] Fix all remaining warnings. --- AsyncDisplayKit/ASCollectionView.h | 82 ++++++++++++++-- AsyncDisplayKit/ASDisplayNode.h | 6 +- AsyncDisplayKit/ASTableView.h | 94 +++++++++++++++++-- AsyncDisplayKit/Layout/ASCenterLayoutSpec.h | 11 ++- AsyncDisplayKit/Layout/ASDimension.h | 7 +- AsyncDisplayKit/Layout/ASLayout.h | 23 ++++- AsyncDisplayKit/Layout/ASLayoutable.h | 5 + AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 4 + AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 5 + AsyncDisplayKit/Layout/ASStaticLayoutSpec.h | 29 ++++-- 10 files changed, 237 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 8254a95b64..ed2526ff95 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -95,25 +95,95 @@ - (void)reloadData; /** - * Section updating. + * Inserts one or more sections. * - * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before these methods are called. + * @param sections An index set that specifies the sections to insert. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections; + +/** + * Deletes one or more sections. + * + * @param sections An index set that specifies the sections to delete. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)deleteSections:(NSIndexSet *)sections; + +/** + * Reloads the specified sections. + * + * @param sections An index set that specifies the sections to reload. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)reloadSections:(NSIndexSet *)sections; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; /** - * Items updating. + * Inserts items at the locations identified by an array of index paths. * - * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before these methods are called. + * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. */ - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Deletes the items specified by an array of index paths. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to delete. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Reloads the specified items. + * + * @param indexPaths An array of NSIndexPath objects identifying the items to reload. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; + +/** + * Moves the item at a specified location to a destination location. + * + * @param indexPath The index path identifying the item to move. + * + * @param newIndexPath The index path that is the destination of the move for the item. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; /** diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index e67b1fc512..fa052b3db9 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -144,7 +144,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may * be expensive if result is not cached. * - * @see [ASDisplayNode(Subclassing) measureWithSizeRange:] + * @see measureWithSizeRange: * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] */ - (CGSize)measure:(CGSize)constrainedSize; @@ -361,7 +361,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * This method is used to notify the node that it should purge any content that is both expensive to fetch and to * retain in memory. * - * @see ASDisplayNode(Subclassing):clearFetchedData and fetchData + * @see [ASDisplayNode(Subclassing) clearFetchedData] and [ASDisplayNode(Subclassing) fetchData] */ - (void)recursivelyClearFetchedData; @@ -370,7 +370,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * * @discussion Fetches content from remote sources for the current node and all subnodes. * - * @see ASDisplayNode(Subclassing):fetchData and clearFetchedData + * @see [ASDisplayNode(Subclassing) fetchData] and [ASDisplayNode(Subclassing) clearFetchedData] */ - (void)recursivelyFetchData; diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 1e0534c83b..f8d9a60f7d 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -103,25 +103,107 @@ - (void)endUpdates; /** - * Section updating. + * Inserts one or more sections, with an option to animate the insertion. * - * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before these methods are called. + * @param sections An index set that specifies the sections to insert. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes one or more sections, with an option to animate the deletion. + * + * @param sections An index set that specifies the sections to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified sections using a given animation effect. + * + * @param sections An index set that specifies the sections to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves a section to a new location. + * + * @param section The index of the section to move. + * + * @param newSection The index that is the destination of the move for the section. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; /** - * Row updating. + * Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion. * - * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before these methods are called. + * @param indexPaths An array of NSIndexPath objects, each representing a row index and section index that together identify a row. + * + * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. */ - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Deletes the rows specified by an array of index paths, with an option to animate the deletion. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to delete. + * + * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Reloads the specified rows using a given animation effect. + * + * @param indexPaths An array of NSIndexPath objects identifying the rows to reload. + * + * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; + +/** + * Moves the row at a specified location to a destination location. + * + * @param indexPath The index path identifying the row to move. + * + * @param newIndexPath The index path that is the destination of the move for the row. + * + * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) + * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * before this method is called. + */ - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; /** diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index 5afc73197a..84e015ac57 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -10,6 +10,7 @@ #import +/** How the child is centered within the spec. */ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecCenteringOptions) { /** The child is positioned in {0,0} relatively to the layout bounds */ ASCenterLayoutSpecCenteringNone = 0, @@ -21,6 +22,7 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecCenteringOptions) { ASCenterLayoutSpecCenteringXY = ASCenterLayoutSpecCenteringX | ASCenterLayoutSpecCenteringY }; +/** How much space the spec will take up. */ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { /** The spec will take up the maximum size possible */ ASCenterLayoutSpecSizingOptionDefault, @@ -36,8 +38,13 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { @interface ASCenterLayoutSpec : ASLayoutSpec /** - @param centeringOptions, see ASCenterLayoutSpecCenteringOptions. - @param child The child to center. + * Initializer. + * + * @param centeringOptions How the child is centered. + * + * @param sizingOptions How much space will be taken up. + * + * @param child The child to center. */ + (instancetype)newWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions diff --git a/AsyncDisplayKit/Layout/ASDimension.h b/AsyncDisplayKit/Layout/ASDimension.h index 6c11aa7701..590f9486de 100644 --- a/AsyncDisplayKit/Layout/ASDimension.h +++ b/AsyncDisplayKit/Layout/ASDimension.h @@ -13,14 +13,11 @@ /** A dimension relative to constraints to be provided in the future. - A RelativeDimension can be one of two types: - - "Points" - Just a number. It will always resolve to exactly this amount. This is the default type. - - "Percent" - Multiplied to a provided parent amount to resolve a final amount. */ typedef NS_ENUM(NSInteger, ASRelativeDimensionType) { + /** Just a number. It will always resolve to exactly this amount. This is the default type. */ ASRelativeDimensionTypePoints, + /** Multiplied to a provided parent amount to resolve a final amount. */ ASRelativeDimensionTypePercent, }; diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 3cc5158840..a3d32feddc 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -32,16 +32,33 @@ extern BOOL CGPointIsNull(CGPoint point); */ @property (nonatomic, readonly) NSArray *sublayouts; +/** + * Initializer. + * + * @param layoutableObject The backing ASLayoutable object. + * + * @param size The size of this layout. + * + * @param position The posiion of this layout within its parent (if available). + * + * @param sublayouts Sublayouts belong to the new layout. + */ + (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size position:(CGPoint)position sublayouts:(NSArray *)sublayouts; /** - * Convenience that has CGPointNull position. + * Convenience initializer that has CGPointNull position. * Best used by ASDisplayNode subclasses that are manually creating a layout for -calculateLayoutThatFits:, * or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree, * or for creating a sublayout of which the position is yet to be determined. + * + * @param layoutableObject The backing ASLayoutable object. + * + * @param size The size of this layout. + * + * @param sublayouts Sublayouts belong to the new layout. */ + (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size @@ -51,6 +68,10 @@ extern BOOL CGPointIsNull(CGPoint point); * Convenience that has CGPointNull position and no sublayouts. * Best used for creating a layout that has no sublayouts, and is either a root one * or a sublayout of which the position is yet to be determined. + * + * @param layoutableObject The backing ASLayoutable object. + * + * @param size The size of this layout. */ + (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index ea0acedabb..30bf7af5e8 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -13,6 +13,11 @@ @class ASLayout; +/** + * 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. + */ @protocol ASLayoutable /** diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 040d8ac01a..6663ad2ae3 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -15,8 +15,12 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { /** Inherit alignment value from containing stack. */ ASStackLayoutAlignSelfAuto, + /** Align to start of cross axis */ ASStackLayoutAlignSelfStart, + /** Align with end of cross axis */ ASStackLayoutAlignSelfEnd, + /** Center on cross axis */ ASStackLayoutAlignSelfCenter, + /** Expand to fill cross axis */ ASStackLayoutAlignSelfStretch, }; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 83322b8de5..29f20603c5 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -10,8 +10,11 @@ #import +/** The direction children are stacked in */ typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { + /** Children are stacked vertically */ ASStackLayoutDirectionVertical, + /** Children are stacked horizontally */ ASStackLayoutDirectionHorizontal, }; @@ -34,6 +37,7 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { ASStackLayoutJustifyContentEnd, }; +/** Orientation of children along cross axis */ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { /** Align children to start of cross axis */ ASStackLayoutAlignItemsStart, @@ -68,6 +72,7 @@ typedef struct { justifyContent determines how children are laid out. For example: + - Suppose stacking direction is Vertical, min-width=100, max-width=300, min-height=200, max-height=500. - All children are laid out with min-width=100, max-width=300, min-height=0, max-height=INFINITY. - If the sum of the childrens' heights is less than 200, children with flexGrow are flexed larger. diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 09815c648b..259c32c4d6 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -11,6 +11,10 @@ #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; @@ -21,20 +25,33 @@ */ @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)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; /** - Convenience 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. + * 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)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; @end -/* - A layout spec that positions children at fixed positions. - - Computes a size that is the union of all childrens' frames. +/** + * A layout spec that positions children at fixed positions. + * + * Computes a size that is the union of all childrens' frames. */ @interface ASStaticLayoutSpec : ASLayoutSpec From 8f83e1ae8cb96401e1e1b0a5a1b5908972ef54bb Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 12 Jul 2015 13:27:24 +0700 Subject: [PATCH 007/145] [appledoc] Should generate docs for layout headers. --- docs/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build.sh b/docs/build.sh index 6605748f6f..c532bb0d6b 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -1,7 +1,7 @@ #!/bin/sh set -e -HEADERS=`ls ../AsyncDisplayKit/*.h ../AsyncDisplayKit/Details/ASRangeController.h` +HEADERS=`ls ../AsyncDisplayKit/*.h ../AsyncDisplayKit/Details/ASRangeController.h ../AsyncDisplayKit/Layout/*.h` rm -rf htdocs appledoc From fd3eb94fcb690cb03191e7a57290fd83e7e56354 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Sun, 12 Jul 2015 21:35:42 +0200 Subject: [PATCH 008/145] Add main thread warnings and setter/getter assert --- AsyncDisplayKit/ASEditableTextNode.h | 5 ++++- AsyncDisplayKit/ASEditableTextNode.mm | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index 903fbbee43..c2b2060cdf 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -19,7 +19,10 @@ #pragma mark - Configuration -//! @abstract Access to underlying UITextView for more configuration options. +/** + @abstract Access to underlying UITextView for more configuration options. + @warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created. + */ @property (nonatomic, readwrite, strong) UITextView *textView; //! @abstract The attributes to apply to new text being entered by the user. diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 84e3cc1572..d4abfb6942 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -41,6 +41,7 @@ { @private // Configuration. + UITextView *_textView; NSDictionary *_typingAttributes; // Core. @@ -134,7 +135,7 @@ [self.view addSubview:_placeholderTextKitComponents.textView]; // Create and configure our text view. - _textKitComponents.textView = _textView; + _textKitComponents.textView = _textView; //_textKitComponents.textView = NO; // Unfortunately there's a bug here with iOS 7 DP5 that causes the text-view to only be one line high when scrollEnabled is NO. rdar://14729288 _textKitComponents.textView.delegate = self; _textKitComponents.textView.editable = YES; @@ -189,6 +190,21 @@ #pragma mark - Configuration @synthesize delegate = _delegate; +#pragma mark - +@dynamic textView; + +- (UITextView *)textView{ + ASDisplayNodeAssertMainThread(); + return _textView; +} + +- (void)setTextView:(UITextView *)textView +{ + ASDisplayNodeAssertMainThread(); + _textView = textView; +} + + #pragma mark - @dynamic typingAttributes; From f010b0f3e3b077abaef10150698869be870e088d Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 13 Jul 2015 14:34:10 -0700 Subject: [PATCH 009/145] Update ASTableView test to be more random and more rigorous. --- AsyncDisplayKitTests/ASTableViewTests.m | 28 +++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 08531ce131..63a9b044d7 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -12,7 +12,7 @@ #define NumberOfSections 10 #define NumberOfRowsPerSection 20 -#define NumberOfReloadIterations 50 +#define NumberOfReloadIterations 250 @interface ASTestTableView : ASTableView @property (atomic, copy) void (^willDeallocBlock)(ASTableView *tableView); @@ -155,28 +155,34 @@ [tableView reloadData]; for (int i = 0; i < NumberOfReloadIterations; ++i) { - UITableViewRowAnimation rowAnimation = (arc4random_uniform(1) == 0 ? UITableViewRowAnimationMiddle : UITableViewRowAnimationNone); - - BOOL animatedScroll = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL reloadRowsInsteadOfSections = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL letRunloopProceed = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL useBeginEndUpdates = (arc4random_uniform(2) == 0 ? YES : NO); + UITableViewRowAnimation rowAnimation = (arc4random_uniform(2) == 0 ? UITableViewRowAnimationMiddle : UITableViewRowAnimationNone); + BOOL animatedScroll = (arc4random_uniform(2) == 0 ? YES : NO); + BOOL reloadRowsInsteadOfSections = (arc4random_uniform(2) == 0 ? YES : NO); + NSTimeInterval runLoopDelay = ((arc4random_uniform(2) == 0) ? (1.0 / (1 + arc4random_uniform(500))) : 0); + BOOL useBeginEndUpdates = (arc4random_uniform(3) == 0 ? YES : NO); + + // instrument our instrumentation ;) + NSLog(@"Iteration %03d: %@|%@|%@|%@|%g", i, (rowAnimation == UITableViewRowAnimationNone) ? @"NONE " : @"MIDDLE", animatedScroll ? @"ASCR" : @" ", reloadRowsInsteadOfSections ? @"ROWS" : @"SECS", useBeginEndUpdates ? @"BEGEND" : @" ", runLoopDelay); if (useBeginEndUpdates) { [tableView beginUpdates]; } if (reloadRowsInsteadOfSections) { - [tableView reloadRowsAtIndexPaths:[self randomIndexPathsExisting:YES] withRowAnimation:rowAnimation]; + NSArray *indexPaths = [self randomIndexPathsExisting:YES]; + //NSLog(@"reloading rows: %@", indexPaths); + [tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:rowAnimation]; } else { - [tableView reloadSections:[self randomIndexSet] withRowAnimation:rowAnimation]; + NSIndexSet *sections = [self randomIndexSet]; + //NSLog(@"reloading sections: %@", sections); + [tableView reloadSections:sections withRowAnimation:rowAnimation]; } [tableView setContentOffset:CGPointMake(0, arc4random_uniform(tableView.contentSize.height - tableView.bounds.size.height)) animated:animatedScroll]; - if (letRunloopProceed) { + if (runLoopDelay > 0) { // Run other stuff on the main queue for between 2ms and 1000ms. - [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:(1 / (1 + arc4random_uniform(500)))]]; + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:runLoopDelay]]; } if (useBeginEndUpdates) { From 13f71d6672afdf8f579cd2700f1b4c52048403d6 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 13 Jul 2015 14:35:54 -0700 Subject: [PATCH 010/145] update Podfile to require FBSnapshotCase 1.8.1 because v2.0+ requires frameworks and we haven't tried enabling them yet. --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index eb7cb4bb47..687ae3c56c 100644 --- a/Podfile +++ b/Podfile @@ -4,5 +4,5 @@ platform :ios, '7.0' target :'AsyncDisplayKitTests', :exclusive => true do pod 'OCMock', '~> 2.2' - pod 'FBSnapshotTestCase' + pod 'FBSnapshotTestCase', '~> 1.8.1' end From 8c72118e6910fb0ba0c05541d4bac314fc5f726e Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Wed, 15 Jul 2015 10:19:03 -0700 Subject: [PATCH 011/145] Don't abuse the CI servers so much. Note - With this configuration we will often pass with a known race issue we currently have. Making ASTableViewStressTest more interesting so we can reliably reproduce the issue there. --- AsyncDisplayKitTests/ASTableViewTests.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 63a9b044d7..154a9eb3ae 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -12,7 +12,7 @@ #define NumberOfSections 10 #define NumberOfRowsPerSection 20 -#define NumberOfReloadIterations 250 +#define NumberOfReloadIterations 50 @interface ASTestTableView : ASTableView @property (atomic, copy) void (^willDeallocBlock)(ASTableView *tableView); @@ -162,7 +162,7 @@ BOOL useBeginEndUpdates = (arc4random_uniform(3) == 0 ? YES : NO); // instrument our instrumentation ;) - NSLog(@"Iteration %03d: %@|%@|%@|%@|%g", i, (rowAnimation == UITableViewRowAnimationNone) ? @"NONE " : @"MIDDLE", animatedScroll ? @"ASCR" : @" ", reloadRowsInsteadOfSections ? @"ROWS" : @"SECS", useBeginEndUpdates ? @"BEGEND" : @" ", runLoopDelay); + //NSLog(@"Iteration %03d: %@|%@|%@|%@|%g", i, (rowAnimation == UITableViewRowAnimationNone) ? @"NONE " : @"MIDDLE", animatedScroll ? @"ASCR" : @" ", reloadRowsInsteadOfSections ? @"ROWS" : @"SECS", useBeginEndUpdates ? @"BEGEND" : @" ", runLoopDelay); if (useBeginEndUpdates) { [tableView beginUpdates]; From 3da7305abb63deb032b46d76cef84553bf63aec8 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Wed, 15 Jul 2015 16:31:01 -0700 Subject: [PATCH 012/145] Wait to begin thrashing until we have completed reloading the data so we aren't trying to do both operations concurrently. --- AsyncDisplayKitTests/ASTableViewTests.m | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 154a9eb3ae..e73ff71e33 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -151,8 +151,19 @@ tableView.asyncDelegate = dataSource; tableView.asyncDataSource = dataSource; + + XCTestExpectation *reloadDataExpectation = [self expectationWithDescription:@"reloadData"]; - [tableView reloadData]; + [tableView reloadDataWithCompletion:^{ + NSLog(@"*** Reload Complete ***"); + [reloadDataExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; for (int i = 0; i < NumberOfReloadIterations; ++i) { UITableViewRowAnimation rowAnimation = (arc4random_uniform(2) == 0 ? UITableViewRowAnimationMiddle : UITableViewRowAnimationNone); From 9762e7cfe64febc4a2450de65c9c5b37aee8c88e Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Thu, 16 Jul 2015 20:26:28 -0700 Subject: [PATCH 013/145] short-circuit editing calls after the asyncDataSource has been set to nil (ASTableView) --- AsyncDisplayKit/ASTableView.mm | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index b5ff1fb48f..bfaded25ab 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -499,12 +499,25 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); + + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + [super beginUpdates]; } - (void)rangeControllerEndUpdates:(ASRangeController *)rangeController completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); + + if (!self.asyncDataSource) { + if (completion) { + completion(NO); + } + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + [super endUpdates]; if (completion) { @@ -577,6 +590,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; @@ -587,6 +604,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; @@ -597,6 +618,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; @@ -607,6 +632,10 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; ASPerformBlockWithoutAnimation(preventAnimation, ^{ [super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; From c14ecd7e99719775e27109e9dfa96b799ec7fa06 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Thu, 16 Jul 2015 20:26:45 -0700 Subject: [PATCH 014/145] short-circuit editing calls after the asyncDataSource has been set to nil (ASCollectionView) --- AsyncDisplayKit/ASCollectionView.mm | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 36a995f90b..ee9648cdc7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -543,6 +543,13 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeControllerEndUpdates:(ASRangeController *)rangeController completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + if (completion) { + completion(NO); + } + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + [super performBatchUpdates:^{ [_batchUpdateBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { block(); @@ -577,6 +584,11 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); + + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super insertItemsAtIndexPaths:indexPaths]; @@ -592,6 +604,10 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super deleteItemsAtIndexPaths:indexPaths]; @@ -607,6 +623,10 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super insertSections:indexSet]; @@ -622,6 +642,10 @@ static BOOL _isInterceptedSelector(SEL sel) { ASDisplayNodeAssertMainThread(); + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + if (_performingBatchUpdates) { [_batchUpdateBlocks addObject:^{ [super deleteSections:indexSet]; From 36b92205ed95a6f88a3c5765950c3330ef7e3ef3 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Sat, 18 Jul 2015 16:10:32 +0200 Subject: [PATCH 015/145] Brace style --- AsyncDisplayKit/ASEditableTextNode.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index d4abfb6942..3339064e89 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -193,7 +193,8 @@ #pragma mark - @dynamic textView; -- (UITextView *)textView{ +- (UITextView *)textView +{ ASDisplayNodeAssertMainThread(); return _textView; } From 6b970e0e4ab3b225fa3475c3affd0af8b287f5f0 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Sat, 18 Jul 2015 16:29:25 +0200 Subject: [PATCH 016/145] Display node assert --- AsyncDisplayKit/ASEditableTextNode.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 3339064e89..ec9350781f 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -196,6 +196,7 @@ - (UITextView *)textView { ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssert(_textView != nil, @"ASEditableTextNode's text view has not been allocated yet — access it in -didLoad or after accessing the .view property instead"); return _textView; } From 3c71289927c1ffad72ebaac48ae8122086c1ca63 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Sat, 18 Jul 2015 16:31:13 +0200 Subject: [PATCH 017/145] lazy creation of textview in accessor after assert --- AsyncDisplayKit/ASEditableTextNode.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index ec9350781f..bc1687afdf 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -196,7 +196,9 @@ - (UITextView *)textView { ASDisplayNodeAssertMainThread(); - ASDisplayNodeAssert(_textView != nil, @"ASEditableTextNode's text view has not been allocated yet — access it in -didLoad or after accessing the .view property instead"); + if (!_textView) { + _textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; + } return _textView; } From f497639124a36b7900e4f2455e913db09900241b Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 20 Jul 2015 14:05:56 -0700 Subject: [PATCH 018/145] Add support automatically adjusting the content offset to UITableView as well as support for performing endUpdates with no animations. Additionally, there are critical bug fixes for ASDataController (begin/end updates delegates not called in correct order) and ASRangeController (failure to fully refresh internal state when inserts or delete are made.) --- AsyncDisplayKit/ASCollectionView.h | 24 +++++ AsyncDisplayKit/ASCollectionView.mm | 25 ++++- AsyncDisplayKit/ASTableView.h | 41 +++++++- AsyncDisplayKit/ASTableView.mm | 97 ++++++++++++++++++- AsyncDisplayKit/Details/ASDataController.h | 6 +- AsyncDisplayKit/Details/ASDataController.mm | 62 ++++++++++-- .../Details/ASFlowLayoutController.mm | 16 +-- AsyncDisplayKit/Details/ASRangeController.h | 6 +- AsyncDisplayKit/Details/ASRangeController.mm | 18 ++-- 9 files changed, 255 insertions(+), 40 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index ed2526ff95..575f22bb71 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -78,6 +78,30 @@ */ @property (nonatomic, assign) CGFloat leadingScreensForBatching; +/** + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. You can call it from background + * thread (it is recommendated) and the UI collection view will be updated asynchronously. The asyncDataSource must be updated + * to reflect the changes before this method is called. + * + * @param animated NO to disable animations for this batch + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @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)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion; + +/** + * Perform a batch of updates asynchronously. You can call it from background thread (it is recommendated) and the UI collection + * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before this method is called. + * + * @param updates The block that performs the relevant insert, delete, reload, or move operations. + * @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)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion; + /** * Reload everything from scratch, destroying the working range and all cached nodes. * diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index ee9648cdc7..29260aa86a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -281,11 +281,16 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark Assertions. -- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion +- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion { [_dataController beginUpdates]; updates(); - [_dataController endUpdatesWithCompletion:completion]; + [_dataController endUpdatesAnimated:animated completion:completion]; +} + +- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion +{ + [self performBatchAnimated:YES updates:updates completion:completion]; } - (void)insertSections:(NSIndexSet *)sections @@ -540,7 +545,7 @@ static BOOL _isInterceptedSelector(SEL sel) _performingBatchUpdates = YES; } -- (void)rangeControllerEndUpdates:(ASRangeController *)rangeController completion:(void (^)(BOOL))completion { +- (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); if (!self.asyncDataSource) { @@ -550,11 +555,21 @@ static BOOL _isInterceptedSelector(SEL sel) return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } + BOOL animationsEnabled = NO; + + if (!animated) { + animationsEnabled = [UIView areAnimationsEnabled]; + [UIView setAnimationsEnabled:NO]; + } + [super performBatchUpdates:^{ [_batchUpdateBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { block(); }]; } completion:^(BOOL finished) { + if (!animated) { + [UIView setAnimationsEnabled:animationsEnabled]; + } if (completion) { completion(finished); } @@ -581,7 +596,7 @@ static BOOL _isInterceptedSelector(SEL sel) return [_dataController nodesAtIndexPaths:indexPaths]; } -- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); @@ -600,7 +615,7 @@ static BOOL _isInterceptedSelector(SEL sel) } } -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index f8d9a60f7d..816a41f0a9 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -93,15 +93,40 @@ */ - (void)reloadData; - /** - * We don't support the these methods for animation yet. - * - * TODO: support animations. + * begins a batch of insert, delete reload and move operations. Batches are asynchronous an thread safe. */ - (void)beginUpdates; + +/** + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. + * 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 asynchronous and thread safe. 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; +/** + * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. + * 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 asynchronous and thread safe. It's important to remeber that the ASTableView will + * be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until + * the completion block is executed. + * + * @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)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; + /** * Inserts one or more sections, with an option to animate the insertion. * @@ -222,6 +247,14 @@ */ - (NSArray *)visibleNodes; +/** + * YES to automatically adjust the contentOffset when cells are inserted or deleted "before" + * visible cells, maintaining the users' visible scroll position. + * + * default is NO. + */ +@property (nonatomic) BOOL automaticallyAdjustsContentOffset; + @end diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index bfaded25ab..9645f30e9e 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -16,6 +16,8 @@ #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) #pragma mark - #pragma mark Proxying. @@ -126,6 +128,9 @@ static BOOL _isInterceptedSelector(SEL sel) ASBatchContext *_batchContext; NSIndexPath *_pendingVisibleIndexPath; + + NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; + CGFloat _contentOffsetAdjustment; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -176,6 +181,8 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _leadingScreensForBatching = 1.0; _batchContext = [[ASBatchContext alloc] init]; + + _automaticallyAdjustsContentOffset = NO; } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -326,9 +333,13 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)endUpdates { - [_dataController endUpdates]; + [self endUpdatesAnimated:YES completion:nil]; } +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; +{ + [_dataController endUpdatesAnimated:animated completion:completion]; +} #pragma mark - #pragma mark Editing @@ -373,6 +384,57 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:UITableViewRowAnimationNone]; } +#pragma mark - +#pragma mark adjust content offset + +- (void)beginAdjustingContentOffset +{ + ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); + _contentOffsetAdjustment = 0; + _contentOffsetAdjustmentTopVisibleRow = self.indexPathsForVisibleRows.firstObject; +} + +- (void)endAdjustingContentOffset +{ + ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); + if (_contentOffsetAdjustment != 0) { + self.contentOffset = CGPointMake(0, self.contentOffset.y+_contentOffsetAdjustment); + } + + _contentOffsetAdjustment = 0; + _contentOffsetAdjustmentTopVisibleRow = nil; +} + +- (void)adjustContentOffsetWithNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths inserting:(BOOL)inserting { + // Maintain the users visible window when inserting or deleteing cells by adjusting the content offset for nodes + // before the visible area. If in a begin/end updates block this will update _contentOffsetAdjustment, otherwise it will + // update self.contentOffset directly. + + ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); + + CGFloat dir = (inserting) ? +1 : -1; + CGFloat adjustment = 0; + NSIndexPath *top = _contentOffsetAdjustmentTopVisibleRow ?: self.indexPathsForVisibleRows.firstObject; + + for (int index=0; index %@", indexPath, newIndexPath); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ + LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; diff --git a/AsyncDisplayKit/Details/ASFlowLayoutController.mm b/AsyncDisplayKit/Details/ASFlowLayoutController.mm index 52b9e6e880..7fce4c636d 100644 --- a/AsyncDisplayKit/Details/ASFlowLayoutController.mm +++ b/AsyncDisplayKit/Details/ASFlowLayoutController.mm @@ -103,16 +103,18 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3; NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; NSArray *completedNodes = [_dataSource completedNodes]; + + ASIndexPath currPath = startPath; - while (!ASIndexPathEqualToIndexPath(startPath, endPath)) { - [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:startPath]]; - startPath.row++; + while (!ASIndexPathEqualToIndexPath(currPath, endPath)) { + [indexPathSet addObject:[NSIndexPath indexPathWithASIndexPath:currPath]]; + currPath.row++; // Once we reach the end of the section, advance to the next one. Keep advancing if the next section is zero-sized. - while (startPath.row >= [(NSArray *)completedNodes[startPath.section] count] && startPath.section < completedNodes.count - 1) { - startPath.row = 0; - startPath.section++; - ASDisplayNodeAssert(startPath.section <= endPath.section, @"startPath should never reach a further section than endPath"); + while (currPath.row >= [(NSArray *)completedNodes[currPath.section] count] && currPath.section < completedNodes.count - 1) { + currPath.row = 0; + currPath.section++; + ASDisplayNodeAssert(currPath.section <= endPath.section, @"currPath should never reach a further section than endPath"); } } diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index d8dd1bf58e..2473f302b2 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -88,7 +88,7 @@ * * @param completion Completion block. */ -- (void)rangeControllerEndUpdates:(ASRangeController * )rangeController completion:(void (^)(BOOL))completion ; +- (void)rangeController:(ASRangeController * )rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion; /** * Fetch nodes at specific index paths. @@ -108,7 +108,7 @@ * * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ -- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** * Called for nodes deletion. @@ -119,7 +119,7 @@ * * @param animationOptions Animation options. See ASDataControllerAnimationOptions. */ -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** * Called for section insertion. diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index f41d9d291c..b22db853bf 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -89,6 +89,12 @@ } NSArray *visibleNodePaths = [_delegate rangeControllerVisibleNodeIndexPaths:self]; + + if ( visibleNodePaths.count == 0) { // if we don't have any visibleNodes currently (scrolled before or after content)... + _queuedRangeUpdate = NO; + return ; // don't do anything for this update, but leave _rangeIsValid to make sure we update it later + } + NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; CGSize viewportSize = [_delegate rangeControllerViewportSize:self]; @@ -104,7 +110,7 @@ // this delegate decide what happens when a node is added or removed from a range id rangeDelegate = _rangeTypeHandlers[rangeKey]; - if ([_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) { + if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) { NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection viewportSize:viewportSize rangeType:rangeType]; // Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths @@ -176,9 +182,9 @@ }); } -- (void)dataControllerEndUpdates:(ASDataController *)dataController completion:(void (^)(BOOL))completion { +- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodePerformBlockOnMainThread(^{ - [_delegate rangeControllerEndUpdates:self completion:completion]; + [_delegate rangeController:self endUpdatesAnimated:animated completion:completion]; }); } @@ -192,14 +198,14 @@ ASDisplayNodePerformBlockOnMainThread(^{ _rangeIsValid = NO; - [_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + [_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); } -- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { +- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodePerformBlockOnMainThread(^{ _rangeIsValid = NO; - [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; + [_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }); } From bf0766169b70aa8f5613362d2ba8470e9858bbce Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 20 Jul 2015 22:08:19 -0700 Subject: [PATCH 019/145] PR Feedback: Require main thread for all edit commands, various cleanup. --- AsyncDisplayKit/ASCollectionView.h | 33 ++++++++------------- AsyncDisplayKit/ASCollectionView.mm | 10 +++++++ AsyncDisplayKit/ASTableView.h | 33 ++++++++------------- AsyncDisplayKit/ASTableView.mm | 14 +++++++-- AsyncDisplayKit/Details/ASDataController.mm | 2 -- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 575f22bb71..a47765bbfa 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -79,9 +79,8 @@ @property (nonatomic, assign) CGFloat leadingScreensForBatching; /** - * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. You can call it from background - * thread (it is recommendated) and the UI collection view will be updated asynchronously. The asyncDataSource must be updated - * to reflect the changes before this method is called. + * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before the update block completes. * * @param animated NO to disable animations for this batch * @param updates The block that performs the relevant insert, delete, reload, or move operations. @@ -92,8 +91,8 @@ - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion; /** - * Perform a batch of updates asynchronously. You can call it from background thread (it is recommendated) and the UI collection - * view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes before this method is called. + * Perform a batch of updates asynchronously. This method must be called from the main thread. + * The asyncDataSource must be updated to reflect the changes before update block completes. * * @param updates The block that performs the relevant insert, delete, reload, or move operations. * @param completion A completion handler block to execute when all of the operations are finished. This block takes a single @@ -123,8 +122,7 @@ * * @param sections An index set that specifies the sections to insert. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections; @@ -134,8 +132,7 @@ * * @param sections An index set that specifies the sections to delete. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteSections:(NSIndexSet *)sections; @@ -145,8 +142,7 @@ * * @param sections An index set that specifies the sections to reload. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadSections:(NSIndexSet *)sections; @@ -158,8 +154,7 @@ * * @param newSection The index that is the destination of the move for the section. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; @@ -169,8 +164,7 @@ * * @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths; @@ -180,8 +174,7 @@ * * @param indexPaths An array of NSIndexPath objects identifying the items to delete. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths; @@ -191,8 +184,7 @@ * * @param indexPaths An array of NSIndexPath objects identifying the items to reload. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths; @@ -204,8 +196,7 @@ * * @param newIndexPath The index path that is the destination of the move for the item. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI collection view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 29260aa86a..6244bdd27b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -283,6 +283,8 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion { + ASDisplayNodeAssertMainThread(); + [_dataController beginUpdates]; updates(); [_dataController endUpdatesAnimated:animated completion:completion]; @@ -295,41 +297,49 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)insertSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController insertSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController deleteSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadSections:(NSIndexSet *)sections { + ASDisplayNodeAssertMainThread(); [_dataController reloadSections:sections withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection { + ASDisplayNodeAssertMainThread(); [_dataController moveSection:section toSection:newSection withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths { + ASDisplayNodeAssertMainThread(); [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone]; } - (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { + ASDisplayNodeAssertMainThread(); [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 816a41f0a9..c8dd0e2b81 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -94,7 +94,7 @@ - (void)reloadData; /** - * begins a batch of insert, delete reload and move operations. Batches are asynchronous an thread safe. + * begins a batch of insert, delete reload and move operations. This method must be called from the main thread. */ - (void)beginUpdates; @@ -102,7 +102,7 @@ * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. * 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 asynchronous and thread safe. It's important to remeber that the ASTableView will + * 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. @@ -116,7 +116,7 @@ * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. * 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 asynchronous and thread safe. It's important to remeber that the ASTableView will + * 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 and are not guaranteed to be reflected in the ASTableView until * the completion block is executed. * @@ -134,8 +134,7 @@ * * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -147,8 +146,7 @@ * * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -160,8 +158,7 @@ * * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; @@ -173,8 +170,7 @@ * * @param newSection The index that is the destination of the move for the section. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; @@ -186,8 +182,7 @@ * * @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -199,8 +194,7 @@ * * @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -212,8 +206,7 @@ * * @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; @@ -225,8 +218,7 @@ * * @param newIndexPath The index path that is the destination of the move for the row. * - * @discussion This operation is asynchronous and thread safe. You can call it from background thread (it is recommendated) - * and the UI table view will be updated asynchronously. The asyncDataSource must be updated to reflect the changes + * @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes * before this method is called. */ - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; @@ -249,7 +241,8 @@ /** * YES to automatically adjust the contentOffset when cells are inserted or deleted "before" - * visible cells, maintaining the users' visible scroll position. + * visible cells, maintaining the users' visible scroll position. Currently this feature tracks insertions, moves and deletions of + * cells, but section edits are ignored. * * default is NO. */ diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 9645f30e9e..6aa33f5141 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -328,6 +328,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)beginUpdates { + ASDisplayNodeAssertMainThread(); [_dataController beginUpdates]; } @@ -338,6 +339,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; { + ASDisplayNodeAssertMainThread(); [_dataController endUpdatesAnimated:animated completion:completion]; } @@ -346,41 +348,49 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController insertSections:sections withAnimationOptions:animation]; } - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController deleteSections:sections withAnimationOptions:animation]; } - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController reloadSections:sections withAnimationOptions:animation]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection { + ASDisplayNodeAssertMainThread(); [_dataController moveSection:section toSection:newSection withAnimationOptions:UITableViewRowAnimationNone]; } - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { + ASDisplayNodeAssertMainThread(); [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation]; } - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath { + ASDisplayNodeAssertMainThread(); [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:UITableViewRowAnimationNone]; } @@ -416,7 +426,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { CGFloat adjustment = 0; NSIndexPath *top = _contentOffsetAdjustmentTopVisibleRow ?: self.indexPathsForVisibleRows.firstObject; - for (int index=0; index Date: Tue, 21 Jul 2015 09:38:40 -0700 Subject: [PATCH 020/145] Update Podfile.lock to match earlier changes to Podfile. --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 4196f05616..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -3,7 +3,7 @@ PODS: - OCMock (2.2.4) DEPENDENCIES: - - FBSnapshotTestCase + - FBSnapshotTestCase (~> 1.8.1) - OCMock (~> 2.2) SPEC CHECKSUMS: From 6b380009414a50f95569aad1dd1df514d03a39a8 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Wed, 22 Jul 2015 11:05:27 +0200 Subject: [PATCH 021/145] A simpler, cleaner implementation --- AsyncDisplayKit/ASEditableTextNode.h | 2 +- AsyncDisplayKit/ASEditableTextNode.mm | 20 ++++---------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index c2b2060cdf..05c2bea790 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -23,7 +23,7 @@ @abstract Access to underlying UITextView for more configuration options. @warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created. */ -@property (nonatomic, readwrite, strong) UITextView *textView; +@property (nonatomic, readonly, strong) UITextView *textView; //! @abstract The attributes to apply to new text being entered by the user. @property (nonatomic, readwrite, strong) NSDictionary *typingAttributes; diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index bc1687afdf..31b63fba91 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -41,7 +41,6 @@ { @private // Configuration. - UITextView *_textView; NSDictionary *_typingAttributes; // Core. @@ -78,7 +77,6 @@ _textKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; _textKitComponents.layoutManager.delegate = self; _wordKerner = [[ASTextNodeWordKerner alloc] init]; - _textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; // Create the placeholder scaffolding. _placeholderTextKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; @@ -135,7 +133,7 @@ [self.view addSubview:_placeholderTextKitComponents.textView]; // Create and configure our text view. - _textKitComponents.textView = _textView; + _textKitComponents.textView = self.textView; //_textKitComponents.textView = NO; // Unfortunately there's a bug here with iOS 7 DP5 that causes the text-view to only be one line high when scrollEnabled is NO. rdar://14729288 _textKitComponents.textView.delegate = self; _textKitComponents.textView.editable = YES; @@ -190,25 +188,15 @@ #pragma mark - Configuration @synthesize delegate = _delegate; -#pragma mark - -@dynamic textView; - - (UITextView *)textView { ASDisplayNodeAssertMainThread(); - if (!_textView) { - _textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; + if (!_textKitComponents.textView) { + _textKitComponents.textView = [[_ASDisabledPanUITextView alloc] initWithFrame:CGRectZero textContainer:_textKitComponents.textContainer]; } - return _textView; + return _textKitComponents.textView; } -- (void)setTextView:(UITextView *)textView -{ - ASDisplayNodeAssertMainThread(); - _textView = textView; -} - - #pragma mark - @dynamic typingAttributes; From 6e13c3ebab18fb6688550412e480bdee929aa381 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Fri, 24 Jul 2015 23:05:14 -0700 Subject: [PATCH 022/145] Attempt to fix travis by specifying cocoapods 0.37.2 explicitly --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f160932b2e..492641fb53 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: objective-c before_install: - brew update - brew reinstall xctool - - gem update cocoapods + - gem update cocoapods -v 0.37.2 - xcrun simctl list install: echo "<3" env: From 9b427576f235b2fa1da8f67e261d61aae3d21037 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Fri, 24 Jul 2015 23:22:57 -0700 Subject: [PATCH 023/145] Attempt to fix travis by specifying cocoapods 0.37.2 explicitly --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 492641fb53..35dc2d00eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: objective-c before_install: - brew update - brew reinstall xctool - - gem update cocoapods -v 0.37.2 + - gem install cocoapods -v 0.37.2 - xcrun simctl list install: echo "<3" env: From 46bbe479ae3a1e2c51fa0a0ad9f3cdc5bd9ad3ff Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Sat, 25 Jul 2015 21:25:40 -0700 Subject: [PATCH 024/145] Implement warning assertion for supplementary views in ASCollectionView. Make sure ASTableViewStressTest passes. It was sometimes testing a scenario UITableView itself doesn't support. --- AsyncDisplayKit/ASCollectionView.mm | 7 +++++++ .../ASTableViewStressTest/Sample/ViewController.m | 13 ++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 6244bdd27b..e13c0d5167 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -33,6 +33,9 @@ static BOOL _isInterceptedSelector(SEL sel) // handled by ASCollectionView node<->cell machinery sel == @selector(collectionView:cellForItemAtIndexPath:) || sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || + + // TODO: Supplementary views are currently not supported. An assertion is triggered if the _asyncDataSource implements this method. + // sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || // handled by ASRangeController sel == @selector(numberOfSectionsInCollectionView:) || @@ -216,6 +219,10 @@ static BOOL _isInterceptedSelector(SEL sel) _proxyDataSource = nil; } else { _asyncDataSource = asyncDataSource; + // TODO: Support supplementary views with ASCollectionView. + if ([_asyncDataSource respondsToSelector:@selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)]) { + ASDisplayNodeAssert(NO, @"ASCollectionView is planned to support supplementary views by September 2015. You can work around this issue by using standard items."); + } _proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; super.dataSource = (id)_proxyDataSource; } diff --git a/examples/ASTableViewStressTest/Sample/ViewController.m b/examples/ASTableViewStressTest/Sample/ViewController.m index 92e3ce7e33..a297d37c42 100644 --- a/examples/ASTableViewStressTest/Sample/ViewController.m +++ b/examples/ASTableViewStressTest/Sample/ViewController.m @@ -110,11 +110,14 @@ for (int i = 0; i < NumberOfReloadIterations; ++i) { UITableViewRowAnimation rowAnimation = (arc4random_uniform(1) == 0 ? UITableViewRowAnimationMiddle : UITableViewRowAnimationNone); - BOOL animatedScroll = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL reloadRowsInsteadOfSections = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL letRunloopProceed = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL addIndexPaths = (arc4random_uniform(1) == 0 ? YES : NO); - BOOL useBeginEndUpdates = (arc4random_uniform(2) == 0 ? YES : NO); + BOOL animatedScroll = (arc4random_uniform(2) == 0 ? YES : NO); + BOOL reloadRowsInsteadOfSections = (arc4random_uniform(2) == 0 ? YES : NO); + BOOL letRunloopProceed = (arc4random_uniform(2) == 0 ? YES : NO); + BOOL useBeginEndUpdates = (arc4random_uniform(3) == 0 ? YES : NO); + + // FIXME: Need to revise the logic to support mutating the data source rather than just reload thrashing. + // UITableView itself does not support deleting a row in the same edit transaction as reloading it, for example. + BOOL addIndexPaths = NO; //(arc4random_uniform(2) == 0 ? YES : NO); if (useBeginEndUpdates) { [_tableView beginUpdates]; From ca5e9e7dc3ef1ec4872dcf68f477d148c35c6e81 Mon Sep 17 00:00:00 2001 From: Connor Montgomery Date: Thu, 30 Jul 2015 15:19:00 -0400 Subject: [PATCH 025/145] Add support for configuring on ASEditableTextNode. --- AsyncDisplayKit/ASEditableTextNode.h | 5 +++++ AsyncDisplayKit/ASEditableTextNode.mm | 9 +++++++++ examples/EditableText/Sample/ViewController.m | 1 + 3 files changed, 15 insertions(+) diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/AsyncDisplayKit/ASEditableTextNode.h index fb3354c8e8..bef4a0a81b 100644 --- a/AsyncDisplayKit/ASEditableTextNode.h +++ b/AsyncDisplayKit/ASEditableTextNode.h @@ -50,6 +50,11 @@ //! @abstract The text input mode used by the receiver's keyboard, if it is visible. This value is undefined if the receiver is not the first responder. @property (nonatomic, readonly) UITextInputMode *textInputMode; +/* + @abstract The returnKeyType of the keyboard. This value defaults to UIReturnKeyDefault. + */ +@property (nonatomic, readwrite) UIReturnKeyType returnKeyType; + /** @abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user. @result YES if the receiver's text view is the first-responder; NO otherwise. diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/AsyncDisplayKit/ASEditableTextNode.mm index 3761134481..9f51c59619 100644 --- a/AsyncDisplayKit/ASEditableTextNode.mm +++ b/AsyncDisplayKit/ASEditableTextNode.mm @@ -77,6 +77,7 @@ _textKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; _textKitComponents.layoutManager.delegate = self; _wordKerner = [[ASTextNodeWordKerner alloc] init]; + _returnKeyType = UIReturnKeyDefault; // Create the placeholder scaffolding. _placeholderTextKitComponents = [ASTextKitComponents componentsWithAttributedSeedString:nil textContainerSize:CGSizeZero]; @@ -138,6 +139,7 @@ _textKitComponents.textView.delegate = self; _textKitComponents.textView.editable = YES; _textKitComponents.textView.typingAttributes = _typingAttributes; + _textKitComponents.textView.returnKeyType = _returnKeyType; _textKitComponents.textView.accessibilityHint = _placeholderTextKitComponents.textStorage.string; configureTextView(_textKitComponents.textView); [self.view addSubview:_textKitComponents.textView]; @@ -346,6 +348,13 @@ return [_textKitComponents.textView textInputMode]; } +- (void)setReturnKeyType:(UIReturnKeyType)returnKeyType +{ + ASDN::MutexLocker l(_textKitLock); + _returnKeyType = returnKeyType; + [_textKitComponents.textView setReturnKeyType:_returnKeyType]; +} + - (BOOL)isFirstResponder { ASDN::MutexLocker l(_textKitLock); diff --git a/examples/EditableText/Sample/ViewController.m b/examples/EditableText/Sample/ViewController.m index 595458ec85..91288dfad5 100644 --- a/examples/EditableText/Sample/ViewController.m +++ b/examples/EditableText/Sample/ViewController.m @@ -31,6 +31,7 @@ // simple editable text node. here we use it synchronously, but it fully supports async layout & display _textNode = [[ASEditableTextNode alloc] init]; + _textNode.returnKeyType = UIReturnKeyDone; _textNode.backgroundColor = [[UIColor lightGrayColor] colorWithAlphaComponent:0.1f]; // with placeholder text (displayed if the user hasn't entered text) From eae76d26b79e26ab50b4ce9368b9946f99eec5d6 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Thu, 30 Jul 2015 16:50:11 -0700 Subject: [PATCH 026/145] Add expectedSize to ASNetworkImageNode ASNetworkImageNode defers to ASImageNode to return its calculatedSize. ASImageNode returns the size of its image. There is a good chance that ASNetworkImageNode hasn't downloaded its image yet when calculatedSize is called, so it returns a size of CGSizeZero. On top of that, it is possible that the size of the image is not actually the size that we wish to display in our node. I've added an "expectedImageSize" property that can be used to determine the calculatedSize of an ASNetworkImageNode. --- AsyncDisplayKit/ASNetworkImageNode.h | 5 +++++ AsyncDisplayKit/ASNetworkImageNode.mm | 23 +++++++++++++++++++++++ examples/Kittens/Sample/KittenNode.mm | 5 ++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 5dc723521e..880202f485 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -51,6 +51,11 @@ */ @property (atomic, strong, readwrite) UIImage *defaultImage; +/** + * The expected size of the image that will download + */ +@property (atomic, assign, readwrite) CGSize expectedImageSize; + /** * The URL of a new image to download and display. * diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index 658176eeee..c37a4953e1 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -24,6 +24,7 @@ NSURL *_URL; UIImage *_defaultImage; + CGSize _expectedImageSize; NSUUID *_cacheUUID; id _imageDownload; @@ -44,6 +45,7 @@ _cache = cache; _downloader = downloader; _shouldCacheImage = YES; + _expectedImageSize = CGSizeZero; return self; } @@ -111,6 +113,18 @@ return _defaultImage; } +-(void)setExpectedImageSize:(CGSize)expectedImageSize +{ + ASDN::MutexLocker l(_lock); + _expectedImageSize = expectedImageSize; +} + +- (CGSize)expectedImageSize +{ + ASDN::MutexLocker l(_lock); + return _expectedImageSize; +} + - (void)setDelegate:(id)delegate { ASDN::MutexLocker l(_lock); @@ -153,6 +167,15 @@ } } +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize +{ + ASDN::MutexLocker l(_lock); + if (CGSizeEqualToSize(CGSizeZero, _expectedImageSize)) + return [super calculateSizeThatFits:constrainedSize]; + else + return _expectedImageSize; +} + #pragma mark - Private methods -- only call with lock. - (void)_cancelImageDownload diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 7e321712cc..678d3c4796 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -83,6 +83,7 @@ static const CGFloat kInnerPadding = 10.0f; _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://placekitten.com/%zd/%zd", (NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.height)]]; + _imageNode.expectedImageSize = CGSizeMake(kImageSize, kImageSize); // _imageNode.contentMode = UIViewContentModeCenter; [self addSubnode:_imageNode]; @@ -131,8 +132,6 @@ static const CGFloat kInnerPadding = 10.0f; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode]; - imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize); _textNode.flexShrink = YES; @@ -145,7 +144,7 @@ static const CGFloat kInnerPadding = 10.0f; .direction = ASStackLayoutDirectionHorizontal, .spacing = kInnerPadding } - children:@[imagePlaceholder, _textNode]]]; + children:@[_imageNode, _textNode]]]; } // With box model, you don't need to override this method, unless you want to add custom logic. From f7a4ca45db1e892d14e73e876afab1741a5f69c4 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 11:31:10 -0700 Subject: [PATCH 027/145] Fix ASTextNodeWordKernerTests for Xcode 7/iOS 9 --- AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm b/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm index dc683fd677..4768197ce0 100644 --- a/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm +++ b/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm @@ -80,7 +80,7 @@ CGFloat expectedWidth = [@" " sizeWithAttributes:@{ NSFontAttributeName : font }].width; CGRect boundingBox = [_layoutManagerDelegate layoutManager:_components.layoutManager boundingBoxForControlGlyphAtIndex:0 forTextContainer:_components.textContainer proposedLineFragment:CGRectZero glyphPosition:CGPointZero characterIndex:0]; - + XCTAssertEqualWithAccuracy(boundingBox.size.width, expectedWidth, FLT_EPSILON, @"Word kerning shouldn't alter the default width of %f. Encountered space width was %f", expectedWidth, boundingBox.size.width); } @@ -111,7 +111,7 @@ } NSGlyphProperty *glyphProperties = (NSGlyphProperty *)malloc(sizeof(NSGlyphProperty) * glyphCount); CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * glyphCount); - NSInteger glyphsToChange = [_layoutManagerDelegate layoutManager:_components.layoutManager shouldGenerateGlyphs:glyphs properties:glyphProperties characterIndexes:characterIndexes font:NULL forGlyphRange:stringRange]; + NSInteger glyphsToChange = [_layoutManagerDelegate layoutManager:_components.layoutManager shouldGenerateGlyphs:glyphs properties:glyphProperties characterIndexes:characterIndexes font:[UIFont systemFontOfSize:12.0] forGlyphRange:stringRange]; free(characterIndexes); free(glyphProperties); free(glyphs); From 15133fdbf73a0489bed14c7982ae9346b25e38c4 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 12:11:19 -0700 Subject: [PATCH 028/145] setNeedsDisplay must be called on main --- AsyncDisplayKit/ASDisplayNode.mm | 16 +++++ AsyncDisplayKit/ASTextNode.mm | 59 +++++++++++++------ .../Private/ASDisplayNodeInternal.h | 1 + Podfile.lock | 2 +- 4 files changed, 58 insertions(+), 20 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 43dbc037fa..8d2566cef8 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -64,6 +64,22 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) } } +void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()) +{ + ASDisplayNodeCAssertNotNil(block, @"block is required"); + if (!block) { + return; + } + + if (node.nodeLoaded) { + ASDisplayNodePerformBlockOnMainThread(^{ + block(); + }); + } else { + block(); + } +} + + (void)initialize { if (self == [ASDisplayNode class]) { diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 0206040c07..e5d4ac2348 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -11,6 +11,7 @@ #import #import #import +#import #import #import #import @@ -202,7 +203,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) _constrainedSize = constrainedSizeForText; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); CGSize rendererSize = [[self _renderer] size]; // Add shadow padding back @@ -337,19 +340,21 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) // We need an entirely new renderer [self _invalidateRenderer]; - // Tell the display node superclasses that the cached layout is incorrect now - [self invalidateCalculatedLayout]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + // Tell the display node superclasses that the cached layout is incorrect now + [self invalidateCalculatedLayout]; - [self setNeedsDisplay]; + [self setNeedsDisplay]; - self.accessibilityLabel = _attributedString.string; + self.accessibilityLabel = _attributedString.string; - if (_attributedString.length == 0) { - // We're not an accessibility element by default if there is no string. - self.isAccessibilityElement = NO; - } else { - self.isAccessibilityElement = YES; - } + if (_attributedString.length == 0) { + // We're not an accessibility element by default if there is no string. + self.isAccessibilityElement = NO; + } else { + self.isAccessibilityElement = YES; + } + }); } #pragma mark - Text Layout @@ -360,7 +365,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) _exclusionPaths = exclusionPaths; [self _invalidateRenderer]; [self invalidateCalculatedLayout]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -897,7 +904,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } _shadowColor = shadowColor; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -911,7 +920,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (!CGSizeEqualToSize(_shadowOffset, shadowOffset)) { _shadowOffset = shadowOffset; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -925,7 +936,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_shadowOpacity != shadowOpacity) { _shadowOpacity = shadowOpacity; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -939,7 +952,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_shadowRadius != shadowRadius) { _shadowRadius = shadowRadius; [self _invalidateShadower]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -981,7 +996,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) if (_truncationMode != truncationMode) { _truncationMode = truncationMode; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } } @@ -994,8 +1011,10 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) { if (_maximumLineCount != maximumLineCount) { _maximumLineCount = maximumLineCount; - [self _invalidateRenderer]; + [self _invalidateRenderer]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ [self setNeedsDisplay]; + }); } } @@ -1010,7 +1029,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) { _composedTruncationString = [self _prepareTruncationStringForDrawing:[self _composedTruncationString]]; [self _invalidateRenderer]; - [self setNeedsDisplay]; + ASDisplayNodeRespectThreadAffinityOfNode(self, ^{ + [self setNeedsDisplay]; + }); } /** diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 39ce70105b..b3df379c26 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -20,6 +20,7 @@ BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); +void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)()); typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { ASDisplayNodeMethodOverrideNone = 0, diff --git a/Podfile.lock b/Podfile.lock index 423c2d2851..cf8e89d790 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.37.2 +COCOAPODS: 0.38.2 From d5de2a22fd6044e2d30b37d8184a8a15e6deb085 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 31 Jul 2015 12:24:04 -0700 Subject: [PATCH 029/145] Text node to gracefully fail upon font failure --- AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m b/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m index 3ea9d596f2..55efebdce8 100644 --- a/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m +++ b/AsyncDisplayKit/Details/ASTextNodeCoreTextAdditions.m @@ -11,6 +11,8 @@ #import #import +#import "ASAssert.h" + #pragma mark - Public BOOL ASAttributeWithNameIsUnsupportedCoreTextAttribute(NSString *attributeName) { @@ -65,8 +67,13 @@ NSDictionary *NSAttributedStringAttributesForCoreTextAttributes(NSDictionary *co CTFontRef coreTextFont = (__bridge CTFontRef)coreTextValue; NSString *fontName = (__bridge_transfer NSString *)CTFontCopyPostScriptName(coreTextFont); CGFloat fontSize = CTFontGetSize(coreTextFont); - - cleanAttributes[NSFontAttributeName] = [UIFont fontWithName:fontName size:fontSize]; + UIFont *font = [UIFont fontWithName:fontName size:fontSize]; + ASDisplayNodeCAssertNotNil(font, @"unable to load font %@ with size %f", fontName, fontSize); + if (font == nil) { + // Gracefully fail if we were unable to load the font. + font = [UIFont systemFontOfSize:fontSize]; + } + cleanAttributes[NSFontAttributeName] = font; } // kCTKernAttributeName -> NSKernAttributeName else if ([coreTextKey isEqualToString:(NSString *)kCTKernAttributeName]) { From 56768a837ab9e3ebee08345822f8bf17fab429e9 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 2 Aug 2015 09:20:46 +0300 Subject: [PATCH 030/145] - Support internal relayout, that is a relayout caused by internal layout changes, like subnodes re-arrangement and/or subnodes' size change. The constrained size applied to root node is unchanged. - Update Kittens example to show how internal relayout is done. --- AsyncDisplayKit/ASDisplayNode.h | 13 ++++++++++- AsyncDisplayKit/ASDisplayNode.mm | 22 +++++++++++++++++++ .../Private/ASDisplayNode+UIViewBridge.mm | 1 + .../Private/ASDisplayNodeInternal.h | 1 + examples/Kittens/Sample/KittenNode.h | 2 ++ examples/Kittens/Sample/KittenNode.mm | 19 ++++++++++++++-- examples/Kittens/Sample/ViewController.m | 14 ++++++++++-- 7 files changed, 67 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index fa052b3db9..fadd242a59 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -506,7 +506,18 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); @interface ASDisplayNode (UIViewBridge) - (void)setNeedsDisplay; // Marks the view as needing display. Convenience for use whether view is created or not, or from a background thread. -- (void)setNeedsLayout; // Marks the view as needing layout. Convenience for use whether view is created or not, or from a background thread. + +/** + * Marks the view as needing layout. Convenience for use whether view is created or not, or from a background thread. + * + * If this node was measured, calling this method triggers an internal relayout: the calculated layout is invalidated, + * and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size. + * + * Note: If the relayout causes a change in size of the root node that is attached to a container view + * (table or collection view, for example), the container view must be notified to redraw. + * In case of table or collection view, calling -beginUpdates and -endUpdates is enough. + */ +- (void)setNeedsLayout; @property (atomic, retain) id contents; // default=nil @property (atomic, assign) BOOL clipsToBounds; // default==NO diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 43dbc037fa..6b6196fd00 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -560,6 +560,28 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) [[self asyncLayer] displayImmediately]; } +- (void)__setNeedsLayout +{ + ASDisplayNodeAssertThreadAffinity(self); + ASDN::MutexLocker l(_propertyLock); + + if (!_flags.isMeasured) { + return; + } + + ASSizeRange oldConstrainedSize = _constrainedSize; + [self invalidateCalculatedLayout]; + + if (_supernode) { + // Cause supernode's layout to be invalidated + [_supernode setNeedsLayout]; + } else { + // This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used. + [self measureWithSizeRange:oldConstrainedSize]; + self.frame = CGRectMake(0.0f, 0.0f, _layout.size.width, _layout.size.height); + } +} + // These private methods ensure that subclasses are not required to call super in order for _renderingSubnodes to be properly managed. - (void)__layout diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm index 2f2994fd98..ef013a97c2 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm @@ -174,6 +174,7 @@ - (void)setNeedsLayout { _bridge_prologue; + [self __setNeedsLayout]; _messageToViewOrLayer(setNeedsLayout); } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 39ce70105b..b59c34d754 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -121,6 +121,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { // Core implementation of -measureWithSizeRange:. Must be called with _propertyLock held. - (ASLayout *)__measureWithSizeRange:(ASSizeRange)constrainedSize; +- (void)__setNeedsLayout; - (void)__layout; - (void)__setSupernode:(ASDisplayNode *)supernode; diff --git a/examples/Kittens/Sample/KittenNode.h b/examples/Kittens/Sample/KittenNode.h index 162cb22a32..3cc23d5a44 100644 --- a/examples/Kittens/Sample/KittenNode.h +++ b/examples/Kittens/Sample/KittenNode.h @@ -19,4 +19,6 @@ - (instancetype)initWithKittenOfSize:(CGSize)size; +- (void)toggleImageEnlargement; + @end diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 7e321712cc..2289fb7416 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -29,6 +29,8 @@ static const CGFloat kInnerPadding = 10.0f; ASNetworkImageNode *_imageNode; ASTextNode *_textNode; ASDisplayNode *_divider; + BOOL _isImageEnlarged; + BOOL _isNodesSwapped; } @end @@ -84,6 +86,7 @@ static const CGFloat kInnerPadding = 10.0f; (NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.height)]]; // _imageNode.contentMode = UIViewContentModeCenter; + [_imageNode addTarget:self action:@selector(toggleNodesSwap) forControlEvents:ASControlNodeEventTouchUpInside]; [self addSubnode:_imageNode]; // lorem ipsum text, plus some nice styling @@ -132,7 +135,7 @@ static const CGFloat kInnerPadding = 10.0f; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode]; - imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize); + imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize * (!_isImageEnlarged ? 1 : 2)); _textNode.flexShrink = YES; @@ -145,7 +148,7 @@ static const CGFloat kInnerPadding = 10.0f; .direction = ASStackLayoutDirectionHorizontal, .spacing = kInnerPadding } - children:@[imagePlaceholder, _textNode]]]; + children:!_isNodesSwapped ? @[imagePlaceholder, _textNode] : @[_textNode, imagePlaceholder]]]; } // With box model, you don't need to override this method, unless you want to add custom logic. @@ -181,4 +184,16 @@ static const CGFloat kInnerPadding = 10.0f; } #endif +- (void)toggleImageEnlargement +{ + _isImageEnlarged = !_isImageEnlarged; + [self setNeedsLayout]; +} + +- (void)toggleNodesSwap +{ + _isNodesSwapped = !_isNodesSwapped; + [self setNeedsLayout]; +} + @end diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index b3293c13fc..12e5123079 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -102,6 +102,16 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell #pragma mark - #pragma mark ASTableView. +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [_tableView deselectRowAtIndexPath:indexPath animated:YES]; + [_tableView beginUpdates]; + // Assume only kitten nodes are selectable (see -tableView:shouldHighlightRowAtIndexPath:). + KittenNode *node = (KittenNode *)[_tableView nodeForRowAtIndexPath:indexPath]; + [node toggleImageEnlargement]; + [_tableView endUpdates]; +} + - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath { // special-case the first row @@ -123,8 +133,8 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { - // disable row selection - return NO; + // Enable selection for kitten nodes + return indexPath.section != 0 || indexPath.row != 0; } - (void)tableViewLockDataSource:(ASTableView *)tableView From 0acfea234f09ca092bb408226524a731c0f15b52 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 3 Aug 2015 02:30:08 +0300 Subject: [PATCH 031/145] Update comments of setNeedsLayout and setNeedsDisplay of ASDisplayNode. --- AsyncDisplayKit/ASDisplayNode.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index fadd242a59..b4236cf32c 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -505,10 +505,13 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ @interface ASDisplayNode (UIViewBridge) -- (void)setNeedsDisplay; // Marks the view as needing display. Convenience for use whether view is created or not, or from a background thread. +/** + * Marks the view as needing display. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. + */ +- (void)setNeedsDisplay; /** - * Marks the view as needing layout. Convenience for use whether view is created or not, or from a background thread. + * Marks the node as needing layout. Convenience for use whether the view / layer is loaded or not. Safe to call from a background thread. * * If this node was measured, calling this method triggers an internal relayout: the calculated layout is invalidated, * and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size. From 77b586a7e63937eca952393d091749e1daa665f3 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Mon, 3 Aug 2015 06:25:23 +0300 Subject: [PATCH 032/145] Improvements on internal relayout. --- AsyncDisplayKit/ASDisplayNode.h | 4 ++-- AsyncDisplayKit/ASDisplayNode.mm | 4 +++- examples/Kittens/Sample/KittenNode.mm | 6 +++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index b4236cf32c..f748693040 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -517,8 +517,8 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * and the supernode is notified or (if this node is the root one) a full measurement pass is executed using the old constrained size. * * Note: If the relayout causes a change in size of the root node that is attached to a container view - * (table or collection view, for example), the container view must be notified to redraw. - * In case of table or collection view, calling -beginUpdates and -endUpdates is enough. + * (table or collection view, for example), the container view must be notified to relayout. + * For ASTableView and ASCollectionView, an empty batch editing transaction must be triggered to animate to new row / item sizes. */ - (void)setNeedsLayout; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 6b6196fd00..d9fe7520bd 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -578,7 +578,9 @@ void ASDisplayNodePerformBlockOnMainThread(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]; - self.frame = CGRectMake(0.0f, 0.0f, _layout.size.width, _layout.size.height); + CGRect bounds = self.bounds; + bounds.size = CGSizeMake(_layout.size.width, _layout.size.height); + self.bounds = bounds; } } diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 2289fb7416..de8d0384e4 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -30,7 +30,7 @@ static const CGFloat kInnerPadding = 10.0f; ASTextNode *_textNode; ASDisplayNode *_divider; BOOL _isImageEnlarged; - BOOL _isNodesSwapped; + BOOL _swappedTextAndImage; } @end @@ -148,7 +148,7 @@ static const CGFloat kInnerPadding = 10.0f; .direction = ASStackLayoutDirectionHorizontal, .spacing = kInnerPadding } - children:!_isNodesSwapped ? @[imagePlaceholder, _textNode] : @[_textNode, imagePlaceholder]]]; + children:!_swappedTextAndImage ? @[imagePlaceholder, _textNode] : @[_textNode, imagePlaceholder]]]; } // With box model, you don't need to override this method, unless you want to add custom logic. @@ -192,7 +192,7 @@ static const CGFloat kInnerPadding = 10.0f; - (void)toggleNodesSwap { - _isNodesSwapped = !_isNodesSwapped; + _swappedTextAndImage = !_swappedTextAndImage; [self setNeedsLayout]; } From 5eb75d6bc79390c9b2f650bd6e40b68e92394185 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 3 Aug 2015 14:36:29 -0700 Subject: [PATCH 033/145] Add support for sectionInset in ASCollectionView in property and delegate form --- AsyncDisplayKit/ASCollectionView.h | 13 +++++++++++++ AsyncDisplayKit/ASCollectionView.mm | 12 ++++++++++-- examples/ASCollectionView/Sample/ViewController.m | 4 ++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index a47765bbfa..7af28f96e3 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -320,6 +320,19 @@ */ - (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView; +/** + * Passthrough support to UICollectionViewDelegateFlowLayout sectionInset behavior. + * + * @param collectionView The sender. + * @param collectionViewLayout The layout object requesting the information. + * #param section The index number of the section whose insets are needed. + * + * @discussion The same rules apply as the UICollectionView implementation, but this can also be used without a UICollectionViewFlowLayout. + * https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewDelegateFlowLayout_protocol/index.html#//apple_ref/occ/intfm/UICollectionViewDelegateFlowLayout/collectionView:layout:insetForSectionAtIndex: + * + */ +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; + @end @interface ASCollectionView (Deprecated) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index e13c0d5167..b5b132dca8 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -118,6 +118,7 @@ static BOOL _isInterceptedSelector(SEL sel) NSMutableArray *_batchUpdateBlocks; BOOL _asyncDataFetchingEnabled; + BOOL _implementsInsetSection; ASBatchContext *_batchContext; } @@ -164,6 +165,8 @@ static BOOL _isInterceptedSelector(SEL sel) _performingBatchUpdates = NO; _batchUpdateBlocks = [NSMutableArray array]; + + _implementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; @@ -511,11 +514,16 @@ static BOOL _isInterceptedSelector(SEL sel) - (CGSize)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { CGSize restrainedSize = self.bounds.size; + UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; + + if (_implementsInsetSection) { + sectionInset = [_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; + } if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { - restrainedSize.width = FLT_MAX; + restrainedSize.width = MAX(0, FLT_MAX - sectionInset.left - sectionInset.right); } else { - restrainedSize.height = FLT_MAX; + restrainedSize.height = MAX(0, FLT_MAX - sectionInset.top - sectionInset.bottom); } return restrainedSize; diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 5e2ab4292a..f87e26f9d7 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -95,4 +95,8 @@ [context completeBatchFetching:YES]; } +- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0); +} + @end From 76e5f794dab8a00d70fc30de36548b884791ecd9 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Mon, 3 Aug 2015 23:47:53 +0200 Subject: [PATCH 034/145] Fixing framework target build issues - missing info plist and headers --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++------ AsyncDisplayKit/Details/ASIndexPath.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 3cab5c824f..462e9f9ea9 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -167,8 +167,8 @@ 299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; }; 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; - 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; }; - 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; }; + 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 430E7C911B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; }; 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 430E7C8E1B4C23F100697A4C /* ASIndexPath.m */; }; 464052201A3F83C40061C0BA /* ASDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -571,8 +571,8 @@ ACF6ED5A1B178DC700DA7C62 /* ASRatioLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRatioLayoutSpecSnapshotTests.mm; sourceTree = ""; }; ACF6ED5B1B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackLayoutSpecSnapshotTests.mm; sourceTree = ""; }; B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B35061DE1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit-iOS.h"; sourceTree = ""; }; + B35061DD1B010EDF0018CF92 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "../AsyncDisplayKit-iOS/Info.plist"; sourceTree = ""; }; + B35061DE1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit-iOS.h"; path = "../AsyncDisplayKit-iOS/AsyncDisplayKit-iOS.h"; sourceTree = ""; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = ""; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = ""; }; D785F6611A74327E00291744 /* ASScrollNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollNode.m; sourceTree = ""; }; @@ -1743,7 +1743,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = AsyncDisplayKit/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -1770,7 +1770,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = AsyncDisplayKit/Info.plist; + INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit-iOS/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; diff --git a/AsyncDisplayKit/Details/ASIndexPath.h b/AsyncDisplayKit/Details/ASIndexPath.h index 9a307817db..a6eb514a4c 100644 --- a/AsyncDisplayKit/Details/ASIndexPath.h +++ b/AsyncDisplayKit/Details/ASIndexPath.h @@ -7,6 +7,7 @@ */ #import +#import typedef struct { NSInteger section; From 26202865ac651f636208dea78feab064337ce1c8 Mon Sep 17 00:00:00 2001 From: Roy Marmelstein Date: Tue, 4 Aug 2015 00:07:30 +0200 Subject: [PATCH 035/145] Adding files that were missing from the framework target --- AsyncDisplayKit.xcodeproj/project.pbxproj | 62 +++++++++++++++++++++ AsyncDisplayKit/Private/ASInternalHelpers.h | 1 + 2 files changed, 63 insertions(+) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 462e9f9ea9..aee3b1867a 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -166,6 +166,37 @@ 299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 299DA1A71A828D2900162D41 /* ASBatchContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; 299DA1AA1A828D2900162D41 /* ASBatchContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 299DA1A81A828D2900162D41 /* ASBatchContext.mm */; }; 29CDC2E21AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 29CDC2E11AAE70D000833CA4 /* ASBasicImageDownloaderContextTests.m */; }; + 34EFC75B1B701BAF00AD841F /* ASDimension.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED071B17843500DA7C62 /* ASDimension.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED081B17843500DA7C62 /* ASDimension.mm */; }; + 34EFC75D1B701BE900AD841F /* ASInternalHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 34EFC75E1B701BF000AD841F /* ASInternalHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.mm */; }; + 34EFC75F1B701C8600AD841F /* ASInsetLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */; }; + 34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; }; + 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */; }; + 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */ = {isa = PBXBuildFile; fileRef = AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7661B701CD200AD841F /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; }; + 34EFC7671B701CD900AD841F /* ASLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED0B1B17843500DA7C62 /* ASLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */; }; + 34EFC7691B701CE100AD841F /* ASLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED111B17843500DA7C62 /* ASLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */; }; + 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC76D1B701CF100AD841F /* ASOverlayLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */; }; + 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */; }; + 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7711B701CFF00AD841F /* ASStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */; }; + 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */; }; + 34EFC7751B701D2400AD841F /* ASStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED471B17847A00DA7C62 /* ASStackPositionedLayout.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */; }; + 34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */; }; + 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = ACF6ED451B17847A00DA7C62 /* ASLayoutSpecUtilities.h */; settings = {ATTRIBUTES = (Private, ); }; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 430E7C8D1B4C23F100697A4C /* ASIndexPath.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1092,10 +1123,12 @@ buildActionMask = 2147483647; files = ( B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */, + 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, B31A241E1B0114FD0016AE7A /* AsyncDisplayKit.h in Headers */, B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */, B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */, + 34EFC7671B701CD900AD841F /* ASLayout.h in Headers */, B35062361B010EFD0018CF92 /* ASTextNodeTypes.h in Headers */, B35062341B010EFD0018CF92 /* ASTextNodeTextKitHelpers.h in Headers */, B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, @@ -1108,15 +1141,19 @@ B35061FD1B010EFD0018CF92 /* ASDisplayNode+Subclasses.h in Headers */, B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */, B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, + 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, B350625C1B010F070018CF92 /* ASLog.h in Headers */, B35062551B010EFD0018CF92 /* ASSentinel.h in Headers */, + 34EFC75B1B701BAF00AD841F /* ASDimension.h in Headers */, B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */, B35062391B010EFD0018CF92 /* ASThread.h in Headers */, B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */, + 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */, + 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */, B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */, B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */, B35061F71B010EFD0018CF92 /* ASCollectionViewProtocols.h in Headers */, @@ -1124,6 +1161,7 @@ B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */, B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */, B35062171B010EFD0018CF92 /* ASDataController.h in Headers */, + 34EFC7711B701CFF00AD841F /* ASStackLayoutSpec.h in Headers */, B350625A1B010F070018CF92 /* ASDisplayNodeExtraIvars.h in Headers */, B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */, B35061DF1B010EDF0018CF92 /* AsyncDisplayKit-iOS.h in Headers */, @@ -1132,14 +1170,21 @@ B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, + 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */, B35062581B010F070018CF92 /* ASAvailability.h in Headers */, B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */, B350622B1B010EFD0018CF92 /* ASRangeHandlerRender.h in Headers */, + 34EFC7751B701D2400AD841F /* ASStackPositionedLayout.h in Headers */, + 34EFC7771B701D2D00AD841F /* ASStackUnpositionedLayout.h in Headers */, B350622E1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.h in Headers */, B35062061B010EFD0018CF92 /* ASNetworkImageNode.h in Headers */, + 34EFC7691B701CE100AD841F /* ASLayoutable.h in Headers */, + 34EFC75F1B701C8600AD841F /* ASInsetLayoutSpec.h in Headers */, + 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, + 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */, B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1147,9 +1192,12 @@ B35061FE1B010EFD0018CF92 /* ASDisplayNodeExtras.h in Headers */, B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */, B35062021B010EFD0018CF92 /* ASImageNode.h in Headers */, + 34EFC75D1B701BE900AD841F /* ASInternalHelpers.h in Headers */, + 34EFC7611B701C9C00AD841F /* ASBackgroundLayoutSpec.h in Headers */, B35062301B010EFD0018CF92 /* ASTextNodeRenderer.h in Headers */, 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */, B350620D1B010EFD0018CF92 /* ASTextNode.h in Headers */, + 34EFC76E1B701CF400AD841F /* ASRatioLayoutSpec.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, B350621B1B010EFD0018CF92 /* ASFlowLayoutController.h in Headers */, B35062291B010EFD0018CF92 /* ASRangeHandlerPreload.h in Headers */, @@ -1458,18 +1506,26 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, B35061FC1B010EFD0018CF92 /* ASDisplayNode.mm in Sources */, B35062181B010EFD0018CF92 /* ASDataController.mm in Sources */, + 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */, B35062501B010EFD0018CF92 /* ASDisplayNode+DebugTiming.mm in Sources */, B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */, B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */, + 34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */, + 34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */, B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */, + 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */, 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */, + 34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */, + 34EFC7661B701CD200AD841F /* ASRelativeSize.mm in Sources */, B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */, + 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */, B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */, @@ -1487,18 +1543,24 @@ B35062071B010EFD0018CF92 /* ASNetworkImageNode.mm in Sources */, B35062011B010EFD0018CF92 /* ASEditableTextNode.mm in Sources */, B35062441B010EFD0018CF92 /* UIView+ASConvenience.m in Sources */, + 34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */, B350622F1B010EFD0018CF92 /* ASTextNodeCoreTextAdditions.m in Sources */, + 34EFC76D1B701CF100AD841F /* ASOverlayLayoutSpec.mm in Sources */, B35062031B010EFD0018CF92 /* ASImageNode.mm in Sources */, B35062091B010EFD0018CF92 /* ASScrollNode.m in Sources */, B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */, 430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */, + 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, B35062381B010EFD0018CF92 /* ASTextNodeWordKerner.m in Sources */, B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */, B35062351B010EFD0018CF92 /* ASTextNodeTextKitHelpers.mm in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */, + 34EFC76B1B701CEB00AD841F /* ASLayoutSpec.mm in Sources */, B35062121B010EFD0018CF92 /* _ASDisplayView.mm in Sources */, + 34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */, B350624C1B010EFD0018CF92 /* _ASPendingState.m in Sources */, + 34EFC75E1B701BF000AD841F /* ASInternalHelpers.mm in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, 509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */, B350622C1B010EFD0018CF92 /* ASRangeHandlerRender.mm in Sources */, diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index c2549b26c0..e20c8c999a 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -9,6 +9,7 @@ */ #include +#import #import "ASBaseDefines.h" ASDISPLAYNODE_EXTERN_C_BEGIN From 496cc43337290c42d9f600a2bc8a62773f0f5910 Mon Sep 17 00:00:00 2001 From: rcancro Date: Mon, 3 Aug 2015 22:09:50 -0700 Subject: [PATCH 036/145] Updates to expectedImageSize * renamed property to requestedLayoutSize * moved property to ASDisplayNode --- AsyncDisplayKit/ASDisplayNode.h | 9 +++ AsyncDisplayKit/ASDisplayNode.mm | 14 +++++ AsyncDisplayKit/ASImageNode.mm | 4 +- AsyncDisplayKit/ASNetworkImageNode.h | 5 -- AsyncDisplayKit/ASNetworkImageNode.mm | 83 ++++++++++----------------- examples/Kittens/Sample/KittenNode.mm | 2 +- 6 files changed, 57 insertions(+), 60 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index fa052b3db9..2bd32d7850 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -185,6 +185,15 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ @property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; +/** + * @abstract Used by some implementations of measureWithSizeRange: to provide an intrisic content size + * to a node when one cannot be computed from its subnodes + * + * @return The requested size of this node + */ +@property (atomic, assign, readwrite) CGSize requestedLayoutSize; + + /** @name Managing the nodes hierarchy */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 43dbc037fa..7ad4f55bcc 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -47,6 +47,7 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize requestedLayoutSize = _requestedLayoutSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -135,6 +136,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) _methodOverrides = overrides; _flexBasis = ASRelativeDimensionUnconstrained; + _requestedLayoutSize = CGSizeZero; } - (id)init @@ -1333,6 +1335,18 @@ static NSInteger incrementIfFound(NSInteger i) { return _constrainedSize; } +-(void)setRequestedLayoutSize:(CGSize)requestedLayoutSize +{ + ASDN::MutexLocker l(_propertyLock); + _requestedLayoutSize = requestedLayoutSize; +} + +- (CGSize)requestedLayoutSize +{ + ASDN::MutexLocker l(_propertyLock); + return _requestedLayoutSize; +} + - (UIImage *)placeholderImage { return nil; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index d7cdebbd94..a9a3a33f78 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -111,7 +111,9 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDN::MutexLocker l(_imageLock); - if (_image) + if (CGSizeEqualToSize(self.requestedLayoutSize, CGSizeZero) == NO) + return CGSizeMake(MIN(constrainedSize.width, self.requestedLayoutSize.width), MIN(constrainedSize.height, self.requestedLayoutSize.height)); + else if (_image) return _image.size; else return CGSizeZero; diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/AsyncDisplayKit/ASNetworkImageNode.h index 880202f485..5dc723521e 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.h +++ b/AsyncDisplayKit/ASNetworkImageNode.h @@ -51,11 +51,6 @@ */ @property (atomic, strong, readwrite) UIImage *defaultImage; -/** - * The expected size of the image that will download - */ -@property (atomic, assign, readwrite) CGSize expectedImageSize; - /** * The URL of a new image to download and display. * diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index c37a4953e1..416426b384 100644 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -18,17 +18,16 @@ ASDN::RecursiveMutex _lock; id _cache; id _downloader; - + // Only access any of these with _lock. __weak id _delegate; - + NSURL *_URL; UIImage *_defaultImage; - CGSize _expectedImageSize; - + NSUUID *_cacheUUID; id _imageDownload; - + BOOL _imageLoaded; } @@ -41,12 +40,11 @@ { if (!(self = [super init])) return nil; - + _cache = cache; _downloader = downloader; _shouldCacheImage = YES; - _expectedImageSize = CGSizeZero; - + return self; } @@ -70,19 +68,19 @@ - (void)setURL:(NSURL *)URL resetToDefault:(BOOL)reset { ASDN::MutexLocker l(_lock); - + if (URL == _URL || [URL isEqual:_URL]) { return; } - + [self _cancelImageDownload]; _imageLoaded = NO; - + _URL = URL; - + if (reset || _URL == nil) self.image = _defaultImage; - + if (self.nodeLoaded && self.layer.superlayer) [self _lazilyLoadImageIfNecessary]; } @@ -96,12 +94,12 @@ - (void)setDefaultImage:(UIImage *)defaultImage { ASDN::MutexLocker l(_lock); - + if (defaultImage == _defaultImage || [defaultImage isEqual:_defaultImage]) { return; } _defaultImage = defaultImage; - + if (!_imageLoaded) { self.image = _defaultImage; } @@ -113,18 +111,6 @@ return _defaultImage; } --(void)setExpectedImageSize:(CGSize)expectedImageSize -{ - ASDN::MutexLocker l(_lock); - _expectedImageSize = expectedImageSize; -} - -- (CGSize)expectedImageSize -{ - ASDN::MutexLocker l(_lock); - return _expectedImageSize; -} - - (void)setDelegate:(id)delegate { ASDN::MutexLocker l(_lock); @@ -140,17 +126,17 @@ - (void)displayWillStart { [super displayWillStart]; - + [self fetchData]; } - (void)clearFetchedData { [super clearFetchedData]; - + { ASDN::MutexLocker l(_lock); - + [self _cancelImageDownload]; self.image = _defaultImage; _imageLoaded = NO; @@ -167,15 +153,6 @@ } } -- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize -{ - ASDN::MutexLocker l(_lock); - if (CGSizeEqualToSize(CGSizeZero, _expectedImageSize)) - return [super calculateSizeThatFits:constrainedSize]; - else - return _expectedImageSize; -} - #pragma mark - Private methods -- only call with lock. - (void)_cancelImageDownload @@ -183,10 +160,10 @@ if (!_imageDownload) { return; } - + [_downloader cancelImageDownloadForIdentifier:_imageDownload]; _imageDownload = nil; - + _cacheUUID = nil; } @@ -208,16 +185,16 @@ if (_URL.isFileURL) { { ASDN::MutexLocker l(_lock); - + dispatch_async(dispatch_get_main_queue(), ^{ _imageLoaded = YES; - + if (self.shouldCacheImage) { self.image = [UIImage imageNamed:_URL.path]; } else { self.image = [UIImage imageWithContentsOfFile:_URL.path]; } - + [_delegate imageNode:self didLoadImage:self.image]; }); } @@ -228,42 +205,42 @@ if (strongSelf == nil) { return; } - + { ASDN::MutexLocker l(strongSelf->_lock); - + if (responseImage != NULL) { strongSelf->_imageLoaded = YES; strongSelf.image = [UIImage imageWithCGImage:responseImage]; } - + strongSelf->_imageDownload = nil; - + strongSelf->_cacheUUID = nil; } - + if (responseImage != NULL) { [strongSelf->_delegate imageNode:strongSelf didLoadImage:strongSelf.image]; } }; - + if (_cache != nil) { NSUUID *cacheUUID = [NSUUID UUID]; _cacheUUID = cacheUUID; - + void (^cacheCompletion)(CGImageRef) = ^(CGImageRef image) { // If the cache UUID changed, that means this request was cancelled. if (![_cacheUUID isEqual:cacheUUID]) { return; } - + if (image == NULL && _downloader != nil) { [self _downloadImageWithCompletion:finished]; } else { finished(image); } }; - + [_cache fetchCachedImageWithURL:_URL callbackQueue:dispatch_get_main_queue() completion:cacheCompletion]; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 678d3c4796..b8f1734117 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -83,7 +83,7 @@ static const CGFloat kInnerPadding = 10.0f; _imageNode.URL = [NSURL URLWithString:[NSString stringWithFormat:@"http://placekitten.com/%zd/%zd", (NSInteger)roundl(_kittenSize.width), (NSInteger)roundl(_kittenSize.height)]]; - _imageNode.expectedImageSize = CGSizeMake(kImageSize, kImageSize); + _imageNode.requestedLayoutSize = CGSizeMake(kImageSize, kImageSize); // _imageNode.contentMode = UIViewContentModeCenter; [self addSubnode:_imageNode]; From c03ed9a8f4581175778667e22d00dea1bfb1f889 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Tue, 4 Aug 2015 08:12:32 -0700 Subject: [PATCH 037/145] ASDisplayNode returns requestedLayoutSize by default --- AsyncDisplayKit/ASDisplayNode.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 7ad4f55bcc..6cc75143fd 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1308,7 +1308,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - return CGSizeZero; + return CGSizeMake(MIN(self.requestedLayoutSize.width, constrainedSize.width), MIN(self.requestedLayoutSize.height, constrainedSize.height)); } - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize From 60ed2bca474f9597c90a842386ba9043af6acf0b Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 5 Aug 2015 17:33:53 +0300 Subject: [PATCH 038/145] Fix _implementsInsetSection of ASCollectionView being check before the asyncDelegate is set. --- AsyncDisplayKit/ASCollectionView.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b5b132dca8..8a0b73ffba 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -166,8 +166,6 @@ static BOOL _isInterceptedSelector(SEL sel) _performingBatchUpdates = NO; _batchUpdateBlocks = [NSMutableArray array]; - _implementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); - [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; return self; @@ -244,10 +242,12 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = nil; _asyncDelegate = nil; _proxyDelegate = nil; + _implementsInsetSection = NO; } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; super.delegate = (id)_proxyDelegate; + _implementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } } From 6275219f7902068f13589d7043f4cb6ff150069c Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Fri, 7 Aug 2015 09:26:53 -0700 Subject: [PATCH 039/145] Revert Cocoapod version update --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index cf8e89d790..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.38.2 +COCOAPODS: 0.37.2 From e657bedd40a18ea019876c36d2c478c3533e187f Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 2 Aug 2015 18:01:28 +0300 Subject: [PATCH 040/145] Automatically relayout cells after orientation changed. --- AsyncDisplayKit/ASCollectionView.mm | 39 ++++++++++++++++++++ AsyncDisplayKit/ASTableView.mm | 38 +++++++++++++++++++ AsyncDisplayKit/Details/ASDataController.h | 6 +++ AsyncDisplayKit/Details/ASDataController.mm | 20 ++++++++++ AsyncDisplayKit/Private/ASInternalHelpers.h | 4 ++ AsyncDisplayKit/Private/ASInternalHelpers.mm | 15 ++++++++ 6 files changed, 122 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8a0b73ffba..b19d41c355 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -15,6 +15,7 @@ #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" +#import "ASInternalHelpers.h" const static NSUInteger kASCollectionViewAnimationNone = UITableViewRowAnimationNone; @@ -121,6 +122,8 @@ static BOOL _isInterceptedSelector(SEL sel) BOOL _implementsInsetSection; ASBatchContext *_batchContext; + + BOOL _pendingRelayoutForAllRows; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -168,6 +171,11 @@ static BOOL _isInterceptedSelector(SEL sel) [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; + if (ASSystemVersionLessThan8()) { + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; + } + return self; } @@ -177,6 +185,11 @@ static BOOL _isInterceptedSelector(SEL sel) // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; + + if (ASSystemVersionLessThan8()) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + } } #pragma mark - @@ -462,6 +475,32 @@ static BOOL _isInterceptedSelector(SEL sel) } +#pragma mark - +#pragma mark Orientation Change Handling + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)deviceOrientationDidChange +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (_pendingRelayoutForAllRows) { + _pendingRelayoutForAllRows = NO; + [self performBatchAnimated:NO updates:^{ + [_dataController relayoutAllRows]; + } completion:nil]; + } +} + + #pragma mark - #pragma mark Batch Fetching diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 6aa33f5141..c32a585a72 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -15,6 +15,7 @@ #import "ASRangeController.h" #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" +#import "ASInternalHelpers.h" //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) @@ -131,6 +132,8 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; + + BOOL _pendingRelayoutForAllRows; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -183,6 +186,11 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _batchContext = [[ASBatchContext alloc] init]; _automaticallyAdjustsContentOffset = NO; + + if (ASSystemVersionLessThan8()) { + [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; + } } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -220,6 +228,11 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; + + if (ASSystemVersionLessThan8()) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; + } } #pragma mark - @@ -343,6 +356,31 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_dataController endUpdatesAnimated:animated completion:completion]; } +#pragma mark - +#pragma mark Orientation Change Handling + +- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)deviceOrientationDidChange +{ + _pendingRelayoutForAllRows = YES; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + if (_pendingRelayoutForAllRows) { + _pendingRelayoutForAllRows = NO; + [self beginUpdates]; + [_dataController relayoutAllRows]; + [self endUpdates]; + } +} + #pragma mark - #pragma mark Editing diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 5cee7b86d0..b2e12b829a 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -154,6 +154,12 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +/** + * Re-measures all loaded nodes. Used for external relayout (relayout that is caused by a change in constrained size of each and every cell node, + * for example, after an orientation change). + */ +- (void)relayoutAllRows; + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4ca124a5be..a41f2d84b3 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -555,6 +555,26 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)relayoutAllRows +{ + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + LOG(@"Edit Command - relayoutRows"); + [_editingTransactionQueue waitUntilAllOperationsAreFinished]; + + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_completedNodes); + NSArray *loadedNodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); + + for (NSUInteger i = 0; i < loadedNodes.count && i < indexPaths.count; i++) { + // TODO: The current implementation does not make use of different constrained sizes per node. + CGSize constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; + ASCellNode *node = loadedNodes[i]; + [node measure:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + } + }]; +} + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index e20c8c999a..8693440eff 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -24,4 +24,8 @@ CGFloat ASCeilPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f); +BOOL ASSystemVersionLessThan8(); + +BOOL ASSystemVersionLessThanVersion(NSString *version); + ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index d161523f2b..f230e1e49c 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -61,3 +61,18 @@ CGFloat ASRoundPixelValue(CGFloat f) { return roundf(f * ASScreenScale()) / ASScreenScale(); } + +BOOL ASSystemVersionLessThan8() +{ + static BOOL lessThan8; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + lessThan8 = ASSystemVersionLessThanVersion(@"8"); + }); + return lessThan8; +} + +BOOL ASSystemVersionLessThanVersion(NSString *version) +{ + return [[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] == NSOrderedAscending; +} From 219b659b38724f1d56d1184fc68be17e68970c17 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sat, 8 Aug 2015 20:54:07 +0300 Subject: [PATCH 041/145] - Automatically relayout cells when editing mode is used. - Add editing mode to Kitten sample. --- AsyncDisplayKit/ASTableView.mm | 36 ++++++++++++++++++++- examples/Kittens/Sample/AppDelegate.m | 2 +- examples/Kittens/Sample/ViewController.m | 41 ++++++++++++++++++++---- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index c32a585a72..f605a10a6b 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -104,18 +104,31 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark ASCellNode<->UITableViewCell bridging. +@protocol _ASTableViewCellDelegate +- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state; +@end + @interface _ASTableViewCell : UITableViewCell +@property (nonatomic, weak) id<_ASTableViewCellDelegate> delegate; +@property (nonatomic) NSIndexPath *indexPath; @end @implementation _ASTableViewCell // TODO add assertions to prevent use of view-backed UITableViewCell properties (eg .textLabel) + +- (void)didTransitionToState:(UITableViewCellStateMask)state +{ + [super didTransitionToState:state]; + [_delegate tableViewCell:self atIndexPath:_indexPath didTransitionToState:state]; +} + @end #pragma mark - #pragma mark ASTableView -@interface ASTableView () { +@interface ASTableView () { _ASTableViewProxy *_proxyDataSource; _ASTableViewProxy *_proxyDelegate; @@ -493,11 +506,13 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier]; if (!cell) { cell = [[_ASTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; + cell.delegate = self; } ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; [_rangeController configureContentView:cell.contentView forCellNode:node]; + cell.indexPath = indexPath; cell.backgroundColor = node.backgroundColor; cell.selectionStyle = node.selectionStyle; @@ -827,4 +842,23 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } } +#pragma mark - _ASTableViewCellDelegate + +- (void)tableViewCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath didTransitionToState:(UITableViewCellStateMask)state +{ + [self beginUpdates]; + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + + CGSize 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.width = MIN(cell.contentView.frame.size.width, constrainedSize.width); + } + + [node measure:constrainedSize]; + node.frame = CGRectMake(0, 0, node.calculatedSize.width, node.calculatedSize.height); + [self endUpdates]; +} + @end diff --git a/examples/Kittens/Sample/AppDelegate.m b/examples/Kittens/Sample/AppDelegate.m index a8e5594780..1dea563b77 100644 --- a/examples/Kittens/Sample/AppDelegate.m +++ b/examples/Kittens/Sample/AppDelegate.m @@ -19,7 +19,7 @@ { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; - self.window.rootViewController = [[ViewController alloc] init]; + self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; [self.window makeKeyAndVisible]; return YES; } diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 12e5123079..02760a6355 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -27,12 +27,13 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell ASTableView *_tableView; // array of boxed CGSizes corresponding to placekitten.com kittens - NSArray *_kittenDataSource; + NSMutableArray *_kittenDataSource; BOOL _dataSourceLocked; + NSIndexPath *_blurbNodeIndexPath; } -@property (nonatomic, strong) NSArray *kittenDataSource; +@property (nonatomic, strong) NSMutableArray *kittenDataSource; @property (atomic, assign) BOOL dataSourceLocked; @end @@ -56,10 +57,16 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell // populate our "data source" with some random kittens _kittenDataSource = [self createLitterWithSize:kLitterSize]; + _blurbNodeIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit + target:self + action:@selector(toggleEditingMode)]; + return self; } -- (NSArray *)createLitterWithSize:(NSInteger)litterSize +- (NSMutableArray *)createLitterWithSize:(NSInteger)litterSize { NSMutableArray *kittens = [NSMutableArray arrayWithCapacity:litterSize]; for (NSInteger i = 0; i < litterSize; i++) { @@ -75,7 +82,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell return kittens; } -- (void)setKittenDataSource:(NSArray *)kittenDataSource { +- (void)setKittenDataSource:(NSMutableArray *)kittenDataSource { ASDisplayNodeAssert(!self.dataSourceLocked, @"Could not update data source when it is locked !"); _kittenDataSource = kittenDataSource; @@ -98,6 +105,11 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell return YES; } +- (void)toggleEditingMode +{ + [_tableView setEditing:!_tableView.editing animated:YES]; +} + #pragma mark - #pragma mark ASTableView. @@ -115,7 +127,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath { // special-case the first row - if (indexPath.section == 0 && indexPath.row == 0) { + if ([_blurbNodeIndexPath compare:indexPath] == NSOrderedSame) { BlurbNode *node = [[BlurbNode alloc] init]; return node; } @@ -134,7 +146,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { // Enable selection for kitten nodes - return indexPath.section != 0 || indexPath.row != 0; + return [_blurbNodeIndexPath compare:indexPath] != NSOrderedSame; } - (void)tableViewLockDataSource:(ASTableView *)tableView @@ -173,7 +185,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell } // add new kittens to the data source & notify table of new indexpaths - _kittenDataSource = [_kittenDataSource arrayByAddingObjectsFromArray:moarKittens]; + [_kittenDataSource addObjectsFromArray:moarKittens]; [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; [context completeBatchFetching:YES]; @@ -183,4 +195,19 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell }); } +- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath +{ + // Enable editing for Kitten nodes + return [_blurbNodeIndexPath compare:indexPath] != NSOrderedSame; +} + +- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath +{ + if (editingStyle == UITableViewCellEditingStyleDelete) { + // Assume only kitten nodes are editable (see -tableView:canEditRowAtIndexPath:). + [_kittenDataSource removeObjectAtIndex:indexPath.row - 1]; + [_tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; + } +} + @end From b58e8344c0b196fca6e434051f97bf7b0ef47790 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Thu, 30 Jul 2015 16:50:11 -0700 Subject: [PATCH 042/145] Add expectedSize to ASNetworkImageNode ASNetworkImageNode defers to ASImageNode to return its calculatedSize. ASImageNode returns the size of its image. There is a good chance that ASNetworkImageNode hasn't downloaded its image yet when calculatedSize is called, so it returns a size of CGSizeZero. On top of that, it is possible that the size of the image is not actually the size that we wish to display in our node. I've added an "expectedImageSize" property that can be used to determine the calculatedSize of an ASNetworkImageNode. --- AsyncDisplayKit/ASDisplayNode.h | 8 ++++++++ AsyncDisplayKit/ASDisplayNode.mm | 15 ++++++++++++++- AsyncDisplayKit/ASImageNode.mm | 4 +++- examples/Kittens/Sample/KittenNode.mm | 6 ++---- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index f748693040..0311a3028d 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -185,6 +185,14 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); */ @property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; +/** + * @abstract Used by some implementations of measureWithSizeRange: to provide an intrisic content size + * to a node when one cannot be computed from its subnodes + * + * @return The preferred frame size of this node + */ +@property (atomic, assign, readwrite) CGSize preferredFrameSize; + /** @name Managing the nodes hierarchy */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index d9fe7520bd..b5cdf6be59 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -47,6 +47,7 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -135,6 +136,7 @@ void ASDisplayNodePerformBlockOnMainThread(void (^block)()) _methodOverrides = overrides; _flexBasis = ASRelativeDimensionUnconstrained; + _preferredFrameSize = CGSizeZero; } - (id)init @@ -1330,7 +1332,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - return CGSizeZero; + return _preferredFrameSize; } - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize @@ -1357,6 +1359,17 @@ static NSInteger incrementIfFound(NSInteger i) { return _constrainedSize; } +- (void)setPreferredFrameSize:(CGSize)preferredFrameSize +{ + ASDN::MutexLocker l(_propertyLock); + _preferredFrameSize = preferredFrameSize; +} + +- (CGSize)preferredFrameSize +{ + ASDN::MutexLocker l(_propertyLock); + return _preferredFrameSize; +} - (UIImage *)placeholderImage { return nil; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index d7cdebbd94..2db88e5fef 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -111,7 +111,9 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDN::MutexLocker l(_imageLock); - if (_image) + if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) + return CGSizeMake(MIN(constrainedSize.width, self.preferredFrameSize.width), MIN(constrainedSize.height, self.preferredFrameSize.height)); + else if (_image) return _image.size; else return CGSizeZero; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index de8d0384e4..a852bcb801 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -134,9 +134,7 @@ static const CGFloat kInnerPadding = 10.0f; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - ASRatioLayoutSpec *imagePlaceholder = [ASRatioLayoutSpec newWithRatio:1.0 child:_imageNode]; - imagePlaceholder.flexBasis = ASRelativeDimensionMakeWithPoints(kImageSize * (!_isImageEnlarged ? 1 : 2)); - + _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); _textNode.flexShrink = YES; return @@ -148,7 +146,7 @@ static const CGFloat kInnerPadding = 10.0f; .direction = ASStackLayoutDirectionHorizontal, .spacing = kInnerPadding } - children:!_swappedTextAndImage ? @[imagePlaceholder, _textNode] : @[_textNode, imagePlaceholder]]]; + children:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]]; } // With box model, you don't need to override this method, unless you want to add custom logic. From c26e9c7e948fbd09adeb1fd582845e98e80c8ac0 Mon Sep 17 00:00:00 2001 From: Shannon Ma Date: Mon, 10 Aug 2015 16:42:02 -0700 Subject: [PATCH 043/145] Fix race in ASDisplayNodeRespectThreadAffinityOfNode --- AsyncDisplayKit/ASDisplayNode.mm | 36 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 8d2566cef8..f043b64c73 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -71,12 +71,16 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) return; } - if (node.nodeLoaded) { - ASDisplayNodePerformBlockOnMainThread(^{ + { + // Hold the lock to avoid a race where the node gets loaded while the block is in-flight. + ASDN::MutexLocker l(node->_propertyLock); + if (node.nodeLoaded) { + ASDisplayNodePerformBlockOnMainThread(^{ + block(); + }); + } else { block(); - }); - } else { - block(); + } } } @@ -121,12 +125,12 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) - (void)_initializeInstance { _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); @@ -149,7 +153,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits; } _methodOverrides = overrides; - + _flexBasis = ASRelativeDimensionUnconstrained; } @@ -157,9 +161,9 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) { if (!(self = [super init])) return nil; - + [self _initializeInstance]; - + return self; } @@ -169,7 +173,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) return nil; ASDisplayNodeAssert([viewClass isSubclassOfClass:[UIView class]], @"should initialize with a subclass of UIView"); - + [self _initializeInstance]; _viewClass = viewClass; _flags.synchronous = ![viewClass isSubclassOfClass:[_ASDisplayView class]]; @@ -181,7 +185,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) { if (!(self = [super init])) return nil; - + ASDisplayNodeAssert([layerClass isSubclassOfClass:[CALayer class]], @"should initialize with a subclass of CALayer"); [self _initializeInstance]; @@ -500,7 +504,7 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } /** - * Core implementation of -displaysAsynchronously. + * Core implementation of -displaysAsynchronously. * Must be called with _propertyLock held. */ - (BOOL)_displaysAsynchronously @@ -1142,7 +1146,7 @@ static NSInteger incrementIfFound(NSInteger i) { [self willEnterHierarchy]; } _flags.isEnteringHierarchy = NO; - + CALayer *layer = self.layer; if (!self.layer.contents) { [layer setNeedsDisplay]; @@ -1426,11 +1430,11 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)layout { ASDisplayNodeAssertMainThread(); - + if (!_flags.isMeasured) { return; } - + // Assume that _layout was flattened and is 1-level deep. for (ASLayout *subnodeLayout in _layout.sublayouts) { ASDisplayNodeAssert([_subnodes containsObject:subnodeLayout.layoutableObject], @"Cached sublayouts must only contain subnodes' layout."); From 4f4a26d2a3a61ce96a0cc66f853afe78610f4fab Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 12 Aug 2015 11:33:40 -0700 Subject: [PATCH 044/145] ASDK now supports non-flow layouts. We shouldn't be assuming a flow layout. --- AsyncDisplayKit/ASCollectionView.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b19d41c355..47fba0de35 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -553,7 +553,10 @@ static BOOL _isInterceptedSelector(SEL sel) - (CGSize)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { CGSize restrainedSize = self.bounds.size; - UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; + UIEdgeInsets sectionInset = UIEdgeInsetsZero; + if ([self.collectionViewLayout respondsToSelector:@selector(sectionInset)]) { + sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; + } if (_implementsInsetSection) { sectionInset = [_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; From 592e0301dd8c2ceb2ba264a1ca0db70170c0dbef Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 12 Aug 2015 11:46:46 -0700 Subject: [PATCH 045/145] cache result of responds to selector --- AsyncDisplayKit/ASCollectionView.mm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 47fba0de35..71bd52b010 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -119,7 +119,8 @@ static BOOL _isInterceptedSelector(SEL sel) NSMutableArray *_batchUpdateBlocks; BOOL _asyncDataFetchingEnabled; - BOOL _implementsInsetSection; + BOOL _asyncDelegateImplementsInsetSection; + BOOL _collectionViewLayoutImplementsInsetSection; ASBatchContext *_batchContext; @@ -175,6 +176,8 @@ static BOOL _isInterceptedSelector(SEL sel) [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; } + + _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; return self; } @@ -255,12 +258,12 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = nil; _asyncDelegate = nil; _proxyDelegate = nil; - _implementsInsetSection = NO; + _asyncDelegateImplementsInsetSection = NO; } else { _asyncDelegate = asyncDelegate; _proxyDelegate = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self]; super.delegate = (id)_proxyDelegate; - _implementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); + _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } } @@ -554,11 +557,11 @@ static BOOL _isInterceptedSelector(SEL sel) { CGSize restrainedSize = self.bounds.size; UIEdgeInsets sectionInset = UIEdgeInsetsZero; - if ([self.collectionViewLayout respondsToSelector:@selector(sectionInset)]) { + if (_collectionViewLayoutImplementsInsetSection) { sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; } - if (_implementsInsetSection) { + if (_asyncDelegateImplementsInsetSection) { sectionInset = [_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; } From a3c2dbf8cdd45ed48072566379d433abbaec4896 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 5 Aug 2015 16:16:01 +0300 Subject: [PATCH 046/145] Table and collection views' async data source can provide different constrained size per node. And because the size is of type ASSizeRange, it has a min size that is very helpful in many cases.. --- AsyncDisplayKit/ASCollectionView.h | 11 +++++++++ AsyncDisplayKit/ASCollectionView.mm | 27 +++++++++++++++++---- AsyncDisplayKit/ASTableView.h | 11 +++++++++ AsyncDisplayKit/ASTableView.mm | 20 +++++++++++---- AsyncDisplayKit/Details/ASDataController.h | 5 ++-- AsyncDisplayKit/Details/ASDataController.mm | 15 ++++++------ 6 files changed, 70 insertions(+), 19 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 7af28f96e3..5d3a9d9bef 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -261,6 +261,17 @@ @optional +/** + * Provides the constrained size range for measuring the node at the index path. + * + * @param collectionView The sender. + * + * @param indexPath The index path of the node. + * + * @returns A constrained size range for layout the node at this index path. + */ +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView 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/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 71bd52b010..d6fa28ebc2 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -121,6 +121,7 @@ static BOOL _isInterceptedSelector(SEL sel) BOOL _asyncDataFetchingEnabled; BOOL _asyncDelegateImplementsInsetSection; BOOL _collectionViewLayoutImplementsInsetSection; + BOOL _asyncDataSourceImplementsConstrainedSizeForNode; ASBatchContext *_batchContext; @@ -234,6 +235,7 @@ static BOOL _isInterceptedSelector(SEL sel) super.dataSource = nil; _asyncDataSource = nil; _proxyDataSource = nil; + _asyncDataSourceImplementsConstrainedSizeForNode = NO; } else { _asyncDataSource = asyncDataSource; // TODO: Support supplementary views with ASCollectionView. @@ -242,6 +244,7 @@ static BOOL _isInterceptedSelector(SEL sel) } _proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; super.dataSource = (id)_proxyDataSource; + _asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0); } } @@ -553,9 +556,21 @@ static BOOL _isInterceptedSelector(SEL sel) return node; } -- (CGSize)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - CGSize restrainedSize = self.bounds.size; + ASSizeRange constrainedSize; + if (_asyncDataSourceImplementsConstrainedSizeForNode) { + constrainedSize = [_asyncDataSource collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; + } else { + CGSize maxSize = self.bounds.size; + if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { + maxSize.width = FLT_MAX; + } else { + maxSize.height = FLT_MAX; + } + constrainedSize = ASSizeRangeMake(CGSizeZero, maxSize); + } + UIEdgeInsets sectionInset = UIEdgeInsetsZero; if (_collectionViewLayoutImplementsInsetSection) { sectionInset = [(UICollectionViewFlowLayout *)self.collectionViewLayout sectionInset]; @@ -566,12 +581,14 @@ static BOOL _isInterceptedSelector(SEL sel) } if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { - restrainedSize.width = MAX(0, FLT_MAX - sectionInset.left - sectionInset.right); + constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right); + constrainedSize.max.width = MAX(0, constrainedSize.max.width - sectionInset.left - sectionInset.right); } else { - restrainedSize.height = MAX(0, FLT_MAX - sectionInset.top - sectionInset.bottom); + constrainedSize.min.height = MAX(0, constrainedSize.min.height - sectionInset.top - sectionInset.bottom); + constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom); } - return restrainedSize; + return constrainedSize; } - (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index c8dd0e2b81..d4527c04f3 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -271,6 +271,17 @@ @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 f605a10a6b..cf45cc5320 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -147,6 +147,8 @@ static BOOL _isInterceptedSelector(SEL sel) CGFloat _contentOffsetAdjustment; BOOL _pendingRelayoutForAllRows; + + BOOL _asyncDataSourceImplementsConstrainedSizeForNode; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -273,10 +275,12 @@ 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); } } @@ -801,9 +805,14 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { return node; } -- (CGSize)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - return CGSizeMake(self.bounds.size.width, FLT_MAX); + if (_asyncDataSourceImplementsConstrainedSizeForNode) { + return [_asyncDataSource tableView:self constrainedSizeForNodeAtIndexPath:indexPath]; + } + + // Default size range + return ASSizeRangeMake(CGSizeZero, CGSizeMake(self.bounds.size.width, FLT_MAX)); } - (void)dataControllerLockDataSource @@ -849,14 +858,15 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [self beginUpdates]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - CGSize constrainedSize = [self dataController:_dataController constrainedSizeForNodeAtIndexPath:indexPath]; + 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.width = MIN(cell.contentView.frame.size.width, constrainedSize.width); + constrainedSize.min.width = MIN(cell.contentView.frame.size.width, constrainedSize.min.width); + constrainedSize.max.width = MIN(cell.contentView.frame.size.width, constrainedSize.max.width); } - [node measure:constrainedSize]; + [node measureWithSizeRange:constrainedSize]; node.frame = CGRectMake(0, 0, node.calculatedSize.width, node.calculatedSize.height); [self endUpdates]; } diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index b2e12b829a..7221d688cf 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -8,6 +8,7 @@ #import #import +#import #import "ASFlowLayoutController.h" @class ASCellNode; @@ -27,9 +28,9 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath; /** - The constrained size for layout. + The constrained size range for layout. */ -- (CGSize)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath; /** Fetch the number of rows in specific section. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index a41f2d84b3..4d3d42fcc7 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -108,17 +108,19 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger j = 0; j < nodes.count && j < indexPaths.count; j += kASDataControllerSizingCountPerProcessor) { NSArray *subIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(j, MIN(kASDataControllerSizingCountPerProcessor, indexPaths.count - j))]; - // TODO: The current implementation does not make use of different constrained sizes per node. - // There should be a fast-path that avoids all of this object creation. + //TODO: There should be a fast-path that avoids all of this object creation. NSMutableArray *nodeBoundSizes = [[NSMutableArray alloc] initWithCapacity:kASDataControllerSizingCountPerProcessor]; [subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { - [nodeBoundSizes addObject:[NSValue valueWithCGSize:[_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]]]; + ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + [nodeBoundSizes addObject:[NSValue valueWithBytes:&constrainedSize objCType:@encode(ASSizeRange)]]; }]; dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { ASCellNode *node = nodes[j + idx]; - [node measure:[nodeBoundSizes[idx] CGSizeValue]]; + ASSizeRange constrainedSize; + [nodeBoundSizes[idx] getValue:&constrainedSize]; + [node measureWithSizeRange:constrainedSize]; node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); }]; }); @@ -566,10 +568,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *loadedNodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); for (NSUInteger i = 0; i < loadedNodes.count && i < indexPaths.count; i++) { - // TODO: The current implementation does not make use of different constrained sizes per node. - CGSize constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; + ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; ASCellNode *node = loadedNodes[i]; - [node measure:constrainedSize]; + [node measureWithSizeRange:constrainedSize]; node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); } }]; From b4e6591ad80131467a000254e9541e0d25741a4c Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 12 Aug 2015 13:43:10 -0700 Subject: [PATCH 047/145] call super to return preferredFrameSize. --- AsyncDisplayKit/ASImageNode.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index 2db88e5fef..cb60239730 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -112,7 +112,7 @@ { ASDN::MutexLocker l(_imageLock); if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) - return CGSizeMake(MIN(constrainedSize.width, self.preferredFrameSize.width), MIN(constrainedSize.height, self.preferredFrameSize.height)); + return [super calculateSizeThatFits:constrainedSize]; else if (_image) return _image.size; else From 3bc692c93847d9af6fb2204e064de9355a2dd328 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 12 Aug 2015 15:29:26 -0700 Subject: [PATCH 048/145] comments --- AsyncDisplayKit/ASDisplayNode.h | 6 ++++-- AsyncDisplayKit/ASImageNode.mm | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 0311a3028d..7fcfbd5999 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -186,8 +186,10 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); @property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout; /** - * @abstract Used by some implementations of measureWithSizeRange: to provide an intrisic content size - * to a node when one cannot be computed from its subnodes + * @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out + * a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content + * size. For example, this property could be set on an ASImageNode to display at a size different from the underlying + * image size. * * @return The preferred frame size of this node */ diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index cb60239730..cf90d2f7fa 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -111,6 +111,7 @@ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { ASDN::MutexLocker l(_imageLock); + // if a preferredFrameSize is set, call the superclass to return that instead of using the image size. if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) return [super calculateSizeThatFits:constrainedSize]; else if (_image) From afffd9cfb2209a1af38fc1f8bf8618204c8a5420 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 12 Aug 2015 16:55:12 -0700 Subject: [PATCH 049/145] Add baseline support to ASStackLayoutSpec --- AsyncDisplayKit/ASDisplayNode.mm | 4 +++ AsyncDisplayKit/ASTextNode.mm | 3 +++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 ++ AsyncDisplayKit/Layout/ASLayoutable.h | 10 +++++++ AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 4 +++ AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 +++ .../Private/ASStackLayoutSpecUtilities.h | 4 +++ .../Private/ASStackPositionedLayout.mm | 27 +++++++++++++++++-- 8 files changed, 56 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 02cf7b3103..e860b1bfc6 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -48,6 +48,8 @@ @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; +@synthesize ascender = _ascender; +@synthesize descender = _descender; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -157,6 +159,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; + _ascender = 0; + _descender = 0; } - (id)init diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index e5d4ac2348..afcc014ef6 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -355,6 +355,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); + + self.ascender = [[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender]; + self.descender = [[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender]; } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 8b3efd6f4d..377fbe144b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -24,6 +24,8 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize ascender = _ascender; +@synthesize descender = _descender; + (instancetype)new { diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 30bf7af5e8..e6f7df7bdc 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -57,6 +57,16 @@ */ @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; + /** * @abstract Calculate a layout based on given size range. * diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 6663ad2ae3..0e6a600ec3 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -23,4 +23,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 29f20603c5..77267eb70d 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -47,6 +47,10 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { ASStackLayoutAlignItemsCenter, /** Expand children to fill cross axis */ 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, }; typedef struct { diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 4f5eff0aeb..8a6f7f5126 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -55,6 +55,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 420258460f..5d32c7b4eb 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -15,9 +15,21 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" +static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, + const ASStackUnpositionedItem &item) { + const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); + if (alignItems == ASStackLayoutAlignItemsBaselineFirst) { + return item.child.ascender; + } else if (alignItems == ASStackLayoutAlignItemsBaselineLast) { + return item.layout.size.height + item.child.descender; + } + return 0; +} + static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, - const CGFloat crossSize) + const CGFloat crossSize, + const CGFloat maxBaseline) { switch (alignment(l.child.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: @@ -27,9 +39,14 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; + case ASStackLayoutAlignItemsBaselineFirst: + return maxBaseline - l.child.ascender; + case ASStackLayoutAlignItemsBaselineLast: + return maxBaseline - baselineForItem(style, l); } } + static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, const CGFloat offset, const ASStackUnpositionedLayout &unpositionedLayout, @@ -44,6 +61,12 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &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); + + // Find the maximum height for the baseline + const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ + return baselineForItem(style, a) < baselineForItem(style, b); + }); + const CGFloat maxBaseLine = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; @@ -53,7 +76,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); return l.layout; }); From 3f982b5c9a7e4a557c102268f11f71b58085ffef Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Wed, 12 Aug 2015 18:03:47 -0700 Subject: [PATCH 050/145] Switch to use only layers for offscreen working range window. ASDK layout and display calls don't depend on UIView in any way, so this will reduce UIView-specific overhead from heirarchy manipulation. --- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index b9cf41c87a..1d47b7d10c 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 @@ -49,7 +53,7 @@ ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); [node recursivelySetDisplaySuspended:YES]; - [node.view removeFromSuperview]; + [node.layer removeFromSuperlayer]; [node recursivelyClearContents]; } From 7249661990085606a11ccdfb30b1b1abdfe2a38e Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 9 Aug 2015 23:18:25 +0300 Subject: [PATCH 051/145] Improve relayout when constrained size of all nodes is changed: - In -layoutSubviews of table and collection views, detect changes that cause a different constrained size for nodes, and trigger relayout immediately. - Orientation change can be handled by this solution. So, no need to observe to its events. - Update Kittens example to support iPad (easier to catch bugs on these devices) and add a title to navigation bar (looks a bit nicer). --- AsyncDisplayKit/ASCollectionView.mm | 38 ++++--------------- AsyncDisplayKit/ASTableView.mm | 33 +++------------- AsyncDisplayKit/Details/ASDataController.h | 4 +- AsyncDisplayKit/Details/ASDataController.mm | 17 ++++----- AsyncDisplayKit/Private/ASInternalHelpers.h | 4 -- AsyncDisplayKit/Private/ASInternalHelpers.mm | 15 -------- .../Kittens/Sample.xcodeproj/project.pbxproj | 2 + examples/Kittens/Sample/ViewController.m | 1 + 8 files changed, 27 insertions(+), 87 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index d6fa28ebc2..8223d5208e 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -125,7 +125,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASBatchContext *_batchContext; - BOOL _pendingRelayoutForAllRows; + CGSize _maxSizeForNodesConstrainedSize; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -170,16 +170,13 @@ static BOOL _isInterceptedSelector(SEL sel) _performingBatchUpdates = NO; _batchUpdateBlocks = [NSMutableArray array]; + + _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; + + _maxSizeForNodesConstrainedSize = self.bounds.size; [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; - if (ASSystemVersionLessThan8()) { - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; - } - - _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; - return self; } @@ -189,11 +186,6 @@ static BOOL _isInterceptedSelector(SEL sel) // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; - - if (ASSystemVersionLessThan8()) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - } } #pragma mark - @@ -480,26 +472,12 @@ static BOOL _isInterceptedSelector(SEL sel) } } - -#pragma mark - -#pragma mark Orientation Change Handling - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - _pendingRelayoutForAllRows = YES; -} - -- (void)deviceOrientationDidChange -{ - _pendingRelayoutForAllRows = YES; -} - - (void)layoutSubviews { [super layoutSubviews]; - if (_pendingRelayoutForAllRows) { - _pendingRelayoutForAllRows = NO; + if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) { + _maxSizeForNodesConstrainedSize = self.bounds.size; [self performBatchAnimated:NO updates:^{ [_dataController relayoutAllRows]; } completion:nil]; @@ -562,7 +540,7 @@ static BOOL _isInterceptedSelector(SEL sel) if (_asyncDataSourceImplementsConstrainedSizeForNode) { constrainedSize = [_asyncDataSource collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; } else { - CGSize maxSize = self.bounds.size; + CGSize maxSize = _maxSizeForNodesConstrainedSize; if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { maxSize.width = FLT_MAX; } else { diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index cf45cc5320..6a9a3bd339 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -146,9 +146,9 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; - BOOL _pendingRelayoutForAllRows; - BOOL _asyncDataSourceImplementsConstrainedSizeForNode; + + CGFloat _maxWidthForNodesConstrainedSize; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -202,10 +202,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _automaticallyAdjustsContentOffset = NO; - if (ASSystemVersionLessThan8()) { - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; - } + _maxWidthForNodesConstrainedSize = self.bounds.size.width; } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -243,11 +240,6 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; - - if (ASSystemVersionLessThan8()) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - } } #pragma mark - @@ -373,25 +365,12 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_dataController endUpdatesAnimated:animated completion:completion]; } -#pragma mark - -#pragma mark Orientation Change Handling - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - _pendingRelayoutForAllRows = YES; -} - -- (void)deviceOrientationDidChange -{ - _pendingRelayoutForAllRows = YES; -} - - (void)layoutSubviews { [super layoutSubviews]; - if (_pendingRelayoutForAllRows) { - _pendingRelayoutForAllRows = NO; + if (_maxWidthForNodesConstrainedSize != self.bounds.size.width) { + _maxWidthForNodesConstrainedSize = self.bounds.size.width; [self beginUpdates]; [_dataController relayoutAllRows]; [self endUpdates]; @@ -812,7 +791,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } // Default size range - return ASSizeRangeMake(CGSizeZero, CGSizeMake(self.bounds.size.width, FLT_MAX)); + return ASSizeRangeMake(CGSizeZero, CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); } - (void)dataControllerLockDataSource diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 7221d688cf..68e586daa8 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -156,8 +156,8 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** - * Re-measures all loaded nodes. Used for external relayout (relayout that is caused by a change in constrained size of each and every cell node, - * for example, after an orientation change). + * Re-measures all loaded nodes. Used to respond to a change in size of the containing view + * (e.g. ASTableView or ASCollectionView after an orientation change). */ - (void)relayoutAllRows; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4d3d42fcc7..4cfc03270d 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -564,15 +564,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_completedNodes); - NSArray *loadedNodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); - - for (NSUInteger i = 0; i < loadedNodes.count && i < indexPaths.count; i++) { - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; - ASCellNode *node = loadedNodes[i]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); - } + [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + ASSizeRange constrainedSize = [_dataSource dataController:self + constrainedSizeForNodeAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; }]; } diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 8693440eff..e20c8c999a 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -24,8 +24,4 @@ CGFloat ASCeilPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f); -BOOL ASSystemVersionLessThan8(); - -BOOL ASSystemVersionLessThanVersion(NSString *version); - ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index f230e1e49c..d161523f2b 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -61,18 +61,3 @@ CGFloat ASRoundPixelValue(CGFloat f) { return roundf(f * ASScreenScale()) / ASScreenScale(); } - -BOOL ASSystemVersionLessThan8() -{ - static BOOL lessThan8; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - lessThan8 = ASSystemVersionLessThanVersion(@"8"); - }); - return lessThan8; -} - -BOOL ASSystemVersionLessThanVersion(NSString *version) -{ - return [[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] == NSOrderedAscending; -} diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index fde58763d9..293b513d1e 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -314,6 +314,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -326,6 +327,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 02760a6355..7989fe15ce 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -59,6 +59,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell _blurbNodeIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + self.title = @"Kittens"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(toggleEditingMode)]; From c7043a193c3499809f5f47ca8ad862dd3358472a Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 13 Aug 2015 07:17:24 +0300 Subject: [PATCH 052/145] When a new preferredFrameSize is set, previously calculated layout should be invalidated so that the size will be picked up in the next layout pass. --- AsyncDisplayKit/ASDisplayNode.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 02cf7b3103..03ff30a1b1 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1382,7 +1382,10 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)setPreferredFrameSize:(CGSize)preferredFrameSize { ASDN::MutexLocker l(_propertyLock); - _preferredFrameSize = preferredFrameSize; + if (! CGSizeEqualToSize(_preferredFrameSize, preferredFrameSize)) { + _preferredFrameSize = preferredFrameSize; + [self invalidateCalculatedLayout]; + } } - (CGSize)preferredFrameSize From 1313c11519000de313857cca743b43a8a5e7f1aa Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 13 Aug 2015 07:18:32 +0300 Subject: [PATCH 053/145] preferredFrameSize should be used even if neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses. --- AsyncDisplayKit/ASDisplayNode.mm | 14 ++++++++------ AsyncDisplayKit/Private/ASDisplayNodeInternal.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 03ff30a1b1..c02c053e25 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -150,8 +150,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesEnded:withEvent:))) { overrides |= ASDisplayNodeMethodOverrideTouchesEnded; } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(calculateSizeThatFits:))) { - overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits; + if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(layoutSpecThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits; } _methodOverrides = overrides; @@ -1332,10 +1332,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - if (_methodOverrides & ASDisplayNodeMethodOverrideCalculateSizeThatFits) { - CGSize size = [self calculateSizeThatFits:constrainedSize.max]; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; - } else { + if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) { id layoutSpec = [self layoutSpecThatFits:constrainedSize]; ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize]; // Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct. @@ -1346,6 +1343,11 @@ static NSInteger incrementIfFound(NSInteger i) { return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { return [_subnodes containsObject:evaluatedLayout.layoutableObject]; }]; + } else { + // If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used, + // assume that the default implementation of -calculateSizeThatFits: returns it. + CGSize size = [self calculateSizeThatFits:constrainedSize.max]; + return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; } } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index c3e476a441..5a8883ed02 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -28,7 +28,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1, ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2, ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3, - ASDisplayNodeMethodOverrideCalculateSizeThatFits = 1 << 4 + ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4 }; @class _ASPendingState; From 344c47234961282953fe04e45aa71a8cfc20e6d9 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 13 Aug 2015 13:53:08 -0400 Subject: [PATCH 054/145] Return CGSizeZero if ASTextNode's text length or attributed string is nil --- AsyncDisplayKit/Details/ASTextNodeRenderer.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm index b17ae2833f..af28bf1719 100644 --- a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm +++ b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm @@ -173,9 +173,13 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3; { ASDN::MutexLocker l(_textKitLock); + if (_attributedString.string.length == 0) { + _calculatedSize = CGSizeZero; + return; + } + [self _initializeTextKitComponentsIfNeeded]; - // Force glyph generation and layout, which may not have happened yet (and // isn't triggered by -usedRectForTextContainer:). [_layoutManager ensureLayoutForTextContainer:_textContainer]; From f06630c55ac9a35428dd701932bb10be70437326 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 13 Aug 2015 13:57:54 -0400 Subject: [PATCH 055/145] Add test coverage for ASTextNodeRenderer to validate nil and empty strings produce CGSizeZero --- AsyncDisplayKitTests/ASTextNodeRendererTests.m | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/AsyncDisplayKitTests/ASTextNodeRendererTests.m b/AsyncDisplayKitTests/ASTextNodeRendererTests.m index a9bf427af4..bcfb7c12ce 100644 --- a/AsyncDisplayKitTests/ASTextNodeRendererTests.m +++ b/AsyncDisplayKitTests/ASTextNodeRendererTests.m @@ -69,6 +69,22 @@ XCTAssertTrue(size.height > 0, @"Should have a nonzero height"); } +- (void)testCalculateSizeWithEmptyString +{ + _attributedString = [[NSAttributedString alloc] initWithString:@""]; + [self setUpRenderer]; + CGSize size = [_renderer size]; + XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, size), @"Empty NSAttributedString should result in CGSizeZero"); +} + +- (void)testCalculateSizeWithNilString +{ + _attributedString = nil; + [self setUpRenderer]; + CGSize size = [_renderer size]; + XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, size), @"Nil NSAttributedString should result in CGSizeZero"); +} + - (void)testNumberOfLines { [self setUpRenderer]; From 176e4962bfad1324328230f53b0d16f2273b41f2 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Fri, 14 Aug 2015 14:41:13 -0700 Subject: [PATCH 056/145] 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 02cf7b3103..32d284f587 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 c3e476a441..309cd08f0b 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 8693440eff..b0530db7f7 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 f230e1e49c..ddfcaf5d82 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 d7564e18e57f3bb6102dc1cf48d0609c5bceda40 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Sat, 15 Aug 2015 08:17:43 -0700 Subject: [PATCH 057/145] Added baseline spacing for vertical stack views. --- AsyncDisplayKit/ASTextNode.mm | 4 ++-- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 ++++ AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 7 +++++++ AsyncDisplayKit/Private/ASStackPositionedLayout.h | 2 ++ AsyncDisplayKit/Private/ASStackPositionedLayout.mm | 6 ++++-- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index afcc014ef6..b46620d565 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -356,8 +356,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - self.ascender = [[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender]; - self.descender = [[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender]; + self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; + self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 77267eb70d..77ce335891 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -62,6 +62,10 @@ typedef struct { ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ 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*/ + BOOL baselineRelativeArrangement; } ASStackLayoutSpecStyle; /** diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 75b699c55b..20ed12590f 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -20,11 +20,13 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" +#import "ASThread.h" @implementation ASStackLayoutSpec { ASStackLayoutSpecStyle _style; std::vector> _children; + ASDN::RecursiveMutex _propertyLock; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -51,6 +53,11 @@ 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()]; + + ASDN::MutexLocker l(_propertyLock); + self.ascender = positionedLayout.ascender; + self.descender = positionedLayout.desender; + return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index 211bda5b11..a4d2718ff3 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,6 +17,8 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; + const CGFloat ascender; + const CGFloat desender; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 5d32c7b4eb..ccdeb0407f 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -77,10 +77,12 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); + + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0;; + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize}; + return {stackedChildren, crossSize, maxBaseLine, maxBaseLine == 0 ? 0 : crossSize - maxBaseLine}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From 11896904a2803689a1ee623445ad60fbdd64093a Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Sat, 15 Aug 2015 09:45:14 -0700 Subject: [PATCH 058/145] fixed vertical spacing bug and typo --- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 2 +- AsyncDisplayKit/Private/ASStackPositionedLayout.h | 2 +- AsyncDisplayKit/Private/ASStackPositionedLayout.mm | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 20ed12590f..5e36099d17 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -56,7 +56,7 @@ ASDN::MutexLocker l(_propertyLock); self.ascender = positionedLayout.ascender; - self.descender = positionedLayout.desender; + self.descender = positionedLayout.descender; return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index a4d2718ff3..b8664a3855 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -18,7 +18,7 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; const CGFloat ascender; - const CGFloat desender; + const CGFloat descender; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index ccdeb0407f..4a78594cee 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -66,7 +66,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ return baselineForItem(style, a) < baselineForItem(style, b); }); - const CGFloat maxBaseLine = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); + const CGFloat maxBaseline = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; @@ -76,13 +76,13 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0;; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseLine, maxBaseLine == 0 ? 0 : crossSize - maxBaseLine}; + return {stackedChildren, crossSize, maxBaseline, maxBaseline == 0 ? 0 : maxBaseline - crossSize}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From 05cb1d734d3ce8f7fb54d0bc9703a67e39859342 Mon Sep 17 00:00:00 2001 From: Sal Date: Sun, 16 Aug 2015 18:24:20 -0400 Subject: [PATCH 059/145] use attributedString.length instead of attributedString.string.length to check for empty strings --- AsyncDisplayKit/Details/ASTextNodeRenderer.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm index af28bf1719..84b290e08e 100644 --- a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm +++ b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm @@ -173,7 +173,7 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3; { ASDN::MutexLocker l(_textKitLock); - if (_attributedString.string.length == 0) { + if (_attributedString.length == 0) { _calculatedSize = CGSizeZero; return; } From f7fb6dc4e71b084257d103b37f3893dcf9092a02 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 17 Aug 2015 12:23:25 -0700 Subject: [PATCH 060/145] Node init performance: Use share instances of UIColor and NSAttributedString for defaults. --- AsyncDisplayKit/ASDisplayNodeExtras.h | 1 + AsyncDisplayKit/ASDisplayNodeExtras.mm | 19 ++++++++++++++++++- AsyncDisplayKit/ASTextNode.mm | 12 +++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.h b/AsyncDisplayKit/ASDisplayNodeExtras.h index bb042be975..5ce6ac2ba5 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.h +++ b/AsyncDisplayKit/ASDisplayNodeExtras.h @@ -60,6 +60,7 @@ extern id ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDi extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c); extern UIColor *ASDisplayNodeDefaultPlaceholderColor(); +extern UIColor *ASDisplayNodeDefaultTintColor(); /** Disable willAppear / didAppear / didDisappear notifications for a sub-hierarchy, then re-enable when done. Nested calls are supported. diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.mm b/AsyncDisplayKit/ASDisplayNodeExtras.mm index b8a4c1a4eb..8d2e261570 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.mm +++ b/AsyncDisplayKit/ASDisplayNodeExtras.mm @@ -126,7 +126,24 @@ extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) UIColor *ASDisplayNodeDefaultPlaceholderColor() { - return [UIColor colorWithWhite:0.95 alpha:1.0]; + static UIColor *defaultPlaceholderColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultPlaceholderColor = [UIColor colorWithWhite:0.95 alpha:1.0]; + }); + return defaultPlaceholderColor; +} + +UIColor *ASDisplayNodeDefaultTintColor() +{ + static UIColor *defaultTintColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; + }); + return defaultTintColor; } #pragma mark - Hierarchy Notifications diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index e5d4ac2348..68f4f1fdab 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -126,7 +126,7 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.needsDisplayOnBoundsChange = YES; _truncationMode = NSLineBreakByWordWrapping; - _truncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + _truncationAttributedString = DefaultTruncationAttributedString(); // The common case is for a text node to be non-opaque and blended over some background. self.opaque = NO; @@ -965,6 +965,16 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) #pragma mark - Truncation Message +static NSAttributedString *DefaultTruncationAttributedString() +{ + static NSAttributedString *defaultTruncationAttributedString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTruncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + }); + return defaultTruncationAttributedString; +} + - (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString { // No-op if they're exactly equal (avoid redrawing) From 60cf43766b8064ab26832f60df6eba9f27c2e962 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Mon, 17 Aug 2015 17:19:51 -0700 Subject: [PATCH 061/145] Revert "Switch to use only layers for offscreen working range window." There is an unsolved issue with this optimization that may cause crashes in certain cases. Reverting until it's properly resolved. It doesn't appear to be possible to cause the issue in the test apps used to develop it. This reverts commit 3f982b5c9a7e4a557c102268f11f71b58085ffef. --- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 1d47b7d10c..b9cf41c87a 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -39,12 +39,8 @@ [node recursivelySetDisplaySuspended:NO]; - // 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]; + // add the node to an off-screen window to force display and preserve its contents + [[self.class workingWindow] addSubnode:node]; } - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType @@ -53,7 +49,7 @@ ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); [node recursivelySetDisplaySuspended:YES]; - [node.layer removeFromSuperlayer]; + [node.view removeFromSuperview]; [node recursivelyClearContents]; } From 774d91653bcd0e16fcbc02b646d0fbb645aea086 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 19 Aug 2015 16:59:23 -0700 Subject: [PATCH 062/145] flailing --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 ++ AsyncDisplayKit/ASDisplayNode.h | 4 +- AsyncDisplayKit/ASDisplayNode.mm | 9 +-- AsyncDisplayKit/ASTextNode.mm | 24 +++++++- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 7 ++- AsyncDisplayKit/Layout/ASLayoutable.h | 47 --------------- AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 49 ++++++++++++++-- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 44 +------------- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 17 ++++-- AsyncDisplayKit/Layout/ASStackLayoutable.h | 57 +++++++++++++++++++ .../Private/ASStackLayoutSpecUtilities.h | 4 -- .../Private/ASStackPositionedLayout.h | 3 +- .../Private/ASStackPositionedLayout.mm | 15 ++--- .../Private/ASStackUnpositionedLayout.h | 4 +- .../Private/ASStackUnpositionedLayout.mm | 12 ++-- 16 files changed, 172 insertions(+), 134 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStackLayoutable.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index aee3b1867a..7cff23c102 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,8 @@ 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, ); }; }; + 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, ); }; }; 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, ); }; }; @@ -556,6 +558,7 @@ 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 = ""; }; + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -958,6 +961,7 @@ ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */, ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */, ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */, + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */, AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, @@ -1004,6 +1008,7 @@ files = ( AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */, AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */, + 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */, ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */, ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, @@ -1170,6 +1175,7 @@ B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, + 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */, 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 7fcfbd5999..974bbe1272 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 e860b1bfc6..e14f256b5a 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -48,8 +48,6 @@ @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; -@synthesize ascender = _ascender; -@synthesize descender = _descender; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -159,8 +157,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; - _ascender = 0; - _descender = 0; } - (id)init @@ -457,6 +453,11 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) #pragma mark - +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return 0; +} + - (CGSize)measure:(CGSize)constrainedSize { return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index b46620d565..f15ab5f786 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,6 +17,7 @@ #import #import +#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -105,6 +106,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) ASTextNodeShadower *_shadower; UILongPressGestureRecognizer *_longPressGestureRecognizer; + + CGFloat _topBaseline; + CGFloat _bottomBaseline; } #pragma mark - NSObject @@ -356,8 +360,24 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; - self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; + _topBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + _bottomBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); +} + +#pragma mark - Baseline computation + +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + switch (baselineAlignmentType) { + case ASStackLayoutAlignItemsLastBaseline: + return self.calculatedSize.height + _bottomBaseline; + + case ASStackLayoutAlignItemsFirstBaseline: + return _topBaseline; + + default: + return 0; + } } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index c2602824e7..e92bbf75cc 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,9 +8,9 @@ * */ -#import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 377fbe144b..34f6206e1b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -24,8 +24,6 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; -@synthesize ascender = _ascender; -@synthesize descender = _descender; + (instancetype)new { @@ -36,6 +34,11 @@ return spec; } +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return 0; +} + #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index e6f7df7bdc..4ac01ee05f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -20,53 +20,6 @@ */ @protocol ASLayoutable -/** - * @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; - /** * @abstract Calculate a layout based on given size range. * diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 0e6a600ec3..39e51b65c8 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -8,6 +8,49 @@ * */ +/** The direction children are stacked in */ +typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { + /** Children are stacked vertically */ + ASStackLayoutDirectionVertical, + /** Children are stacked horizontally */ + ASStackLayoutDirectionHorizontal, +}; + +/** If no children are flexible, how should this spec justify its children in the available space? */ +typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { + /** + On overflow, children overflow out of this spec's bounds on the right/bottom side. + On underflow, children are left/top-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentStart, + /** + On overflow, children are centered and overflow on both sides. + On underflow, children are centered within this spec's bounds in the stacking direction. + */ + ASStackLayoutJustifyContentCenter, + /** + On overflow, children overflow out of this spec's bounds on the left/top side. + On underflow, children are right/bottom-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentEnd, +}; + +/** Orientation of children along cross axis */ +typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { + /** Align children to start of cross axis */ + ASStackLayoutAlignItemsStart, + /** Align children with end of cross axis */ + ASStackLayoutAlignItemsEnd, + /** Center children on cross axis */ + ASStackLayoutAlignItemsCenter, + /** Expand children to fill cross axis */ + ASStackLayoutAlignItemsStretch, + /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsFirstBaseline, + /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsLastBaseline, +}; + /** Each child may override their parent stack's cross axis alignment. @see ASStackLayoutAlignItems @@ -23,8 +66,6 @@ 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, + + /** Note: All children in a stack must have the same baseline align type */ }; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 77ce335891..242d6e3122 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -9,49 +9,7 @@ */ #import - -/** The direction children are stacked in */ -typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { - /** Children are stacked vertically */ - ASStackLayoutDirectionVertical, - /** Children are stacked horizontally */ - ASStackLayoutDirectionHorizontal, -}; - -/** If no children are flexible, how should this spec justify its children in the available space? */ -typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { - /** - On overflow, children overflow out of this spec's bounds on the right/bottom side. - On underflow, children are left/top-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentStart, - /** - On overflow, children are centered and overflow on both sides. - On underflow, children are centered within this spec's bounds in the stacking direction. - */ - ASStackLayoutJustifyContentCenter, - /** - On overflow, children overflow out of this spec's bounds on the left/top side. - On underflow, children are right/bottom-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentEnd, -}; - -/** Orientation of children along cross axis */ -typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { - /** Align children to start of cross axis */ - ASStackLayoutAlignItemsStart, - /** Align children with end of cross axis */ - ASStackLayoutAlignItemsEnd, - /** Center children on cross axis */ - ASStackLayoutAlignItemsCenter, - /** Expand children to fill cross axis */ - 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, -}; +#import typedef struct { /** Specifies the direction children are stacked in. */ diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 5e36099d17..90d2cc7fd8 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -25,8 +25,9 @@ @implementation ASStackLayoutSpec { ASStackLayoutSpecStyle _style; - std::vector> _children; + std::vector> _children; ASDN::RecursiveMutex _propertyLock; + CGFloat _distanceToBaseline; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -34,8 +35,10 @@ ASStackLayoutSpec *spec = [super new]; if (spec) { spec->_style = style; - spec->_children = std::vector>(); - for (id child in children) { + spec->_children = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"child must conform to ASStackLayoutable"); + spec->_children.push_back(child); } } @@ -55,12 +58,16 @@ NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; ASDN::MutexLocker l(_propertyLock); - self.ascender = positionedLayout.ascender; - self.descender = positionedLayout.descender; + _distanceToBaseline = positionedLayout.distanceToBaseline; return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return _distanceToBaseline; +} + @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h new file mode 100644 index 0000000000..643bed0382 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -0,0 +1,57 @@ +/* + * 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 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 in stack spec. The distance from the top of the object to its baseline. + */ +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType; + +@end diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 8a6f7f5126..4f5eff0aeb 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -55,10 +55,6 @@ 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.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index b8664a3855..ea9f56fd26 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,8 +17,7 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; - const CGFloat ascender; - const CGFloat descender; + const CGFloat distanceToBaseline; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 4a78594cee..1e667abd73 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -18,10 +18,8 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &item) { const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsBaselineFirst) { - return item.child.ascender; - } else if (alignItems == ASStackLayoutAlignItemsBaselineLast) { - return item.layout.size.height + item.child.descender; + if (alignItems == ASStackLayoutAlignItemsFirstBaseline || alignItems == ASStackLayoutAlignItemsLastBaseline) { + return [item.child distanceToBaseline:alignItems]; } return 0; } @@ -39,9 +37,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - case ASStackLayoutAlignItemsBaselineFirst: - return maxBaseline - l.child.ascender; - case ASStackLayoutAlignItemsBaselineLast: + case ASStackLayoutAlignItemsLastBaseline: + case ASStackLayoutAlignItemsFirstBaseline: return maxBaseline - baselineForItem(style, l); } } @@ -78,11 +75,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.layout.size.height - [l.child distanceToBaseline:style.alignItems] : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseline, maxBaseline == 0 ? 0 : maxBaseline - crossSize}; + return {stackedChildren, crossSize, maxBaseline}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 45f648192e..44b7770ccf 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -15,7 +15,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -30,7 +30,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 9eb83a5b0d..0c094bedfd 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,7 +18,7 @@ /** 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, @@ -186,7 +186,7 @@ static std::function isFlexibleInViolatio } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { return child.flexGrow && child.flexShrink; } @@ -195,7 +195,7 @@ ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) 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 +283,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,7 +292,7 @@ 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 { + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); @@ -312,7 +312,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) { From e7ffe67e3c5357b1391a40e0bf48465afb02fb2e Mon Sep 17 00:00:00 2001 From: rcancro Date: Wed, 19 Aug 2015 22:03:02 -0700 Subject: [PATCH 063/145] new stack layout spec for text --- AsyncDisplayKit.xcodeproj/project.pbxproj | 14 +- AsyncDisplayKit/ASTextNode.h | 2 +- AsyncDisplayKit/ASTextNode.mm | 26 +--- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 - AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 10 -- AsyncDisplayKit/Layout/ASStackLayoutable.h | 10 +- .../Layout/ASStackTextLayoutSpec.h | 36 +++++ .../Layout/ASStackTextLayoutSpec.mm | 127 ++++++++++++++++++ .../Private/ASStackPositionedLayout.h | 1 - .../Private/ASStackPositionedLayout.mm | 10 +- 10 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h create mode 100644 AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 7cff23c102..29c10b3ca5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,10 @@ 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, ); }; }; + 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; + 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; @@ -447,7 +451,7 @@ 058D09D5195D050800B7D73C /* ASControlNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASControlNode.h; sourceTree = ""; }; 058D09D6195D050800B7D73C /* ASControlNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNode.m; sourceTree = ""; }; 058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Subclasses.h"; sourceTree = ""; }; - 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; }; 058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDisplayNode.mm; sourceTree = ""; }; 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeExtras.h; sourceTree = ""; }; @@ -558,6 +562,8 @@ 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 = ""; }; + 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; + 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -967,6 +973,8 @@ ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1074,6 +1082,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, + 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1148,6 +1157,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, + 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1451,6 +1461,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, + 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1512,6 +1523,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index fc520c1232..681c2cb71e 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -30,7 +30,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 f15ab5f786..8b3158a905 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -106,11 +106,11 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) ASTextNodeShadower *_shadower; UILongPressGestureRecognizer *_longPressGestureRecognizer; - - CGFloat _topBaseline; - CGFloat _bottomBaseline; } +@synthesize ascender = _ascender; +@synthesize descender = _descender; + #pragma mark - NSObject - (instancetype)init @@ -360,24 +360,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - _topBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - _bottomBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); -} - -#pragma mark - Baseline computation - -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - switch (baselineAlignmentType) { - case ASStackLayoutAlignItemsLastBaseline: - return self.calculatedSize.height + _bottomBaseline; - - case ASStackLayoutAlignItemsFirstBaseline: - return _topBaseline; - - default: - return 0; - } + _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/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 34f6206e1b..8b3efd6f4d 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -34,11 +34,6 @@ return spec; } -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return 0; -} - #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 90d2cc7fd8..c5c3660fc4 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -26,8 +26,6 @@ { ASStackLayoutSpecStyle _style; std::vector> _children; - ASDN::RecursiveMutex _propertyLock; - CGFloat _distanceToBaseline; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -57,17 +55,9 @@ const CGSize finalSize = directionSize(_style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; - ASDN::MutexLocker l(_propertyLock); - _distanceToBaseline = positionedLayout.distanceToBaseline; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return _distanceToBaseline; -} - @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 643bed0382..057ca8b0fd 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -49,9 +49,11 @@ */ @property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; -/** - * @abstract Used for baseline alignment in stack spec. The distance from the top of the object to its baseline. - */ -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType; +@end + +@protocol ASStackTextLayoutable + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; @end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h new file mode 100644 index 0000000000..ab5b80db19 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h @@ -0,0 +1,36 @@ +// +// ASStackTextLayoutSpec.h +// AsyncDisplayKit +// +// Created by ricky cancro on 8/19/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +/** Orientation of children along cross axis */ +typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { + ASStackTextLayoutBaselineAlignmentNone, + /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ + ASStackTextLayoutBaselineAlignmentFirst, + /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ + ASStackTextLayoutBaselineAlignmentLast, +}; + + +typedef struct { + /** Specifies the direction children are stacked in. */ + ASStackLayoutSpecStyle stackLayoutStyle; + + ASStackTextLayoutBaselineAlignment baselineAlignment; +} ASStackTextLayoutSpecStyle; + +@interface ASStackTextLayoutSpec : ASLayoutSpec + +/** + @param style Specifies how children are laid out. + @param children ASLayoutable children to be positioned. + */ ++ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; + +@end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm new file mode 100644 index 0000000000..a15f4c6686 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm @@ -0,0 +1,127 @@ +// +// ASStackTextLayoutSpec.m +// AsyncDisplayKit +// +// Created by ricky cancro on 8/19/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASStackTextLayoutSpec.h" +#import "ASStackLayoutable.h" + +#import +#import + +#import "ASBaseDefines.h" +#import "ASInternalHelpers.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" +#import "ASStackPositionedLayout.h" +#import "ASStackUnpositionedLayout.h" +#import "ASThread.h" + +static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, + const ASLayout *layout) { + + __weak id textChild = (id) layout.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + case ASStackTextLayoutBaselineAlignmentFirst: + return textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return textChild.descender; + } + +} + +static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, + const ASLayout *l, + const CGFloat maxBaseline) +{ + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentFirst: + case ASStackTextLayoutBaselineAlignmentLast: + return maxBaseline - baselineForItem(style, l); + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + } +} + + +@implementation ASStackTextLayoutSpec +{ + ASStackTextLayoutSpecStyle _textStyle; + std::vector> _children; + std::vector> _stackChildren; + ASDN::RecursiveMutex _propertyLock; +} + ++ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children +{ + ASDisplayNodeAssert(style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone, @"if you don't need baseline alignment, use ASStackLayoutSpec"); + + ASStackTextLayoutSpec *spec = [super new]; + if (spec) { + spec->_textStyle = style; + spec->_children = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + + spec->_children.push_back(child); + spec->_stackChildren.push_back(child); + } + } + return spec; +} + ++ (instancetype)new +{ + ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); +} + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); + + // Alter the positioned layouts to include baselines + const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return baselineForItem(_textStyle, a) < baselineForItem(_textStyle, b); + }); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(_textStyle, *baselineIt); + + CGPoint p = CGPointZero; + BOOL first = YES; + auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ + __weak id textChild = (id) l.layoutableObject; + if (first) { + p = l.position; + } + first = NO; + + if (stackStyle.direction == ASStackLayoutDirectionHorizontal) { + l.position = p + CGPointMake(0, baselineOffset(_textStyle, l, maxBaseline)); + } + + CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + [(id)l.layoutableObject spacingAfter] + spacingAfterBaseline, 0); + + return l; + }); + + const ASStackPositionedLayout alteredPositionedLayouts = {stackedChildren, positionedLayout.crossSize}; + const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, alteredPositionedLayouts.crossSize); + + NSArray *sublayouts = [NSArray arrayWithObjects:&alteredPositionedLayouts.sublayouts[0] count:alteredPositionedLayouts.sublayouts.size()]; + + + return [ASLayout newWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; +} +@end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index ea9f56fd26..211bda5b11 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,7 +17,6 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; - const CGFloat distanceToBaseline; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 1e667abd73..b567be3ef7 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -18,8 +18,10 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &item) { const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsFirstBaseline || alignItems == ASStackLayoutAlignItemsLastBaseline) { - return [item.child distanceToBaseline:alignItems]; + if (alignItems == ASStackLayoutAlignItemsFirstBaseline) { + return item.child.layoutInsets.top; + } else if (alignItems == ASStackLayoutAlignItemsLastBaseline) { + return item.child.layoutInsets.bottom; } return 0; } @@ -75,11 +77,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.layout.size.height - [l.child distanceToBaseline:style.alignItems] : 0; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.layoutInsets.bottom : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseline}; + return {stackedChildren, crossSize}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From f404231b35c337109e6d3e9dfcc7615863b59764 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Thu, 20 Aug 2015 10:38:54 -0700 Subject: [PATCH 064/145] moved baseline alignment to a layout spec. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 14 ++ AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 8 +- .../Layout/ASStackTextLayoutSpec.h | 51 ++++--- .../Layout/ASStackTextLayoutSpec.mm | 140 ++++++------------ .../Private/ASStackPositionedLayout.mm | 30 +--- .../Private/ASStackTextPositionedLayout.h | 26 ++++ .../Private/ASStackTextPositionedLayout.mm | 118 +++++++++++++++ 7 files changed, 243 insertions(+), 144 deletions(-) create mode 100644 AsyncDisplayKit/Private/ASStackTextPositionedLayout.h create mode 100644 AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 29c10b3ca5..83427c715c 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,10 @@ 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 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; + 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; @@ -562,6 +566,8 @@ 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 /* ASStackTextPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackTextPositionedLayout.h; sourceTree = ""; }; + 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackTextPositionedLayout.mm; sourceTree = ""; }; 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; @@ -696,7 +702,9 @@ 058D09AD195D04C000B7D73C /* Products */, FD40E2760492F0CAAEAD552D /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 058D09AD195D04C000B7D73C /* Products */ = { isa = PBXGroup; @@ -928,6 +936,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */, + 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -1056,6 +1066,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, + 9C204A641B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1148,6 +1159,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, + 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1423,6 +1435,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, + 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1526,6 +1539,7 @@ 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, + 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 39e51b65c8..d7737f7eab 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -44,11 +44,7 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { /** Center children on cross axis */ ASStackLayoutAlignItemsCenter, /** Expand children to fill cross axis */ - ASStackLayoutAlignItemsStretch, - /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ - ASStackLayoutAlignItemsFirstBaseline, - /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ - ASStackLayoutAlignItemsLastBaseline, + ASStackLayoutAlignItemsStretch }; /** @@ -66,6 +62,4 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { ASStackLayoutAlignSelfCenter, /** Expand to fill cross axis */ ASStackLayoutAlignSelfStretch, - - /** Note: All children in a stack must have the same baseline align type */ }; diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h index ab5b80db19..a7b94da03b 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h @@ -1,35 +1,48 @@ -// -// ASStackTextLayoutSpec.h -// AsyncDisplayKit -// -// Created by ricky cancro on 8/19/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 -/** Orientation of children along cross axis */ typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { - ASStackTextLayoutBaselineAlignmentNone, - /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ - ASStackTextLayoutBaselineAlignmentFirst, - /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ - ASStackTextLayoutBaselineAlignmentLast, + /** No baseline alignment. This is only valid for a vertical stack */ + ASStackTextLayoutBaselineAlignmentNone, + /** Align all children to the first baseline. This is only valid for a horizontal stack */ + ASStackTextLayoutBaselineAlignmentFirst, + /** 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 */ + ASStackTextLayoutBaselineAlignmentLast, }; typedef struct { - /** Specifies the direction children are stacked in. */ - ASStackLayoutSpecStyle stackLayoutStyle; - - ASStackTextLayoutBaselineAlignment baselineAlignment; + /** Describes how the stack will be laid out */ + ASStackLayoutSpecStyle stackLayoutStyle; + + /** The type of baseline alignment */ + ASStackTextLayoutBaselineAlignment baselineAlignment; } ASStackTextLayoutSpecStyle; -@interface ASStackTextLayoutSpec : ASLayoutSpec + +/** + A specialized version of a stack layout that aligns its children on a baseline. This spec only works with + ASStackTextLayoutable 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 ASStackTextLayoutSpec : ASLayoutSpec /** @param style Specifies how children are laid out. - @param children ASLayoutable children to be positioned. + @param children ASTextLayoutable children to be positioned. */ + (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm index a15f4c6686..67c6942696 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm @@ -1,10 +1,12 @@ -// -// ASStackTextLayoutSpec.m -// AsyncDisplayKit -// -// Created by ricky cancro on 8/19/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 "ASStackTextLayoutSpec.h" #import "ASStackLayoutable.h" @@ -19,109 +21,61 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" +#import "ASStackTextPositionedLayout.h" #import "ASThread.h" -static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, - const ASLayout *layout) { - - __weak id textChild = (id) layout.layoutableObject; - switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentNone: - return 0; - case ASStackTextLayoutBaselineAlignmentFirst: - return textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: - return textChild.descender; - } - -} - -static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, - const ASLayout *l, - const CGFloat maxBaseline) -{ - switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentFirst: - case ASStackTextLayoutBaselineAlignmentLast: - return maxBaseline - baselineForItem(style, l); - case ASStackTextLayoutBaselineAlignmentNone: - return 0; - } -} - @implementation ASStackTextLayoutSpec { - ASStackTextLayoutSpecStyle _textStyle; - std::vector> _children; - std::vector> _stackChildren; - ASDN::RecursiveMutex _propertyLock; + ASStackTextLayoutSpecStyle _textStyle; + std::vector> _stackChildren; + ASDN::RecursiveMutex _propertyLock; } +@synthesize ascender = _ascender; +@synthesize descender = _descender; + + (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children { - ASDisplayNodeAssert(style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone, @"if you don't need baseline alignment, use ASStackLayoutSpec"); - - ASStackTextLayoutSpec *spec = [super new]; - if (spec) { - spec->_textStyle = style; - spec->_children = std::vector>(); - for (id child in children) { - ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); - - spec->_children.push_back(child); - spec->_stackChildren.push_back(child); - } + ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + + ASStackTextLayoutSpec *spec = [super new]; + if (spec) { + spec->_textStyle = style; + spec->_stackChildren = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + + spec->_stackChildren.push_back(child); } - return spec; + } + return spec; } + (instancetype)new { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; - - - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); - const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - - // Alter the positioned layouts to include baselines - const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return baselineForItem(_textStyle, a) < baselineForItem(_textStyle, b); - }); - const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(_textStyle, *baselineIt); - - CGPoint p = CGPointZero; - BOOL first = YES; - auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; - if (first) { - p = l.position; - } - first = NO; - - if (stackStyle.direction == ASStackLayoutDirectionHorizontal) { - l.position = p + CGPointMake(0, baselineOffset(_textStyle, l, maxBaseline)); - } - - CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + [(id)l.layoutableObject spacingAfter] + spacingAfterBaseline, 0); - - return l; - }); - - const ASStackPositionedLayout alteredPositionedLayouts = {stackedChildren, positionedLayout.crossSize}; - const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, alteredPositionedLayouts.crossSize); - - NSArray *sublayouts = [NSArray arrayWithObjects:&alteredPositionedLayouts.sublayouts[0] count:alteredPositionedLayouts.sublayouts.size()]; - - - return [ASLayout newWithLayoutableObject:self - size:ASSizeRangeClamp(constrainedSize, finalSize) - sublayouts:sublayouts]; + ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); + const auto baselinePositionedLayout = ASStackTextPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + + const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); + + NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; + + ASDN::MutexLocker l(_propertyLock); + _ascender = baselinePositionedLayout.ascender; + _descender = baselinePositionedLayout.descender; + + + return [ASLayout newWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; } @end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index b567be3ef7..d106d0618b 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -15,21 +15,9 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" -static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, - const ASStackUnpositionedItem &item) { - const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsFirstBaseline) { - return item.child.layoutInsets.top; - } else if (alignItems == ASStackLayoutAlignItemsLastBaseline) { - return item.child.layoutInsets.bottom; - } - return 0; -} - static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, - const CGFloat crossSize, - const CGFloat maxBaseline) + const CGFloat crossSize) { switch (alignment(l.child.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: @@ -39,9 +27,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - case ASStackLayoutAlignItemsLastBaseline: - case ASStackLayoutAlignItemsFirstBaseline: - return maxBaseline - baselineForItem(style, l); + default: + return 0; } } @@ -61,12 +48,6 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // Find the maximum height for the baseline - const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ - return baselineForItem(style, a) < baselineForItem(style, b); - }); - const CGFloat maxBaseline = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); - CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ @@ -75,10 +56,9 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.layoutInsets.bottom : 0; - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 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/ASStackTextPositionedLayout.h b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h new file mode 100644 index 0000000000..9794e9b807 --- /dev/null +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h @@ -0,0 +1,26 @@ +/* + * 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 "ASLayout.h" +#import "ASDimension.h" +#import "ASStackTextLayoutSpec.h" +#import "ASStackPositionedLayout.h" + +struct ASStackTextPositionedLayout { + 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 ASStackTextPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASStackTextLayoutSpecStyle &textStyle, + const ASSizeRange &constrainedSize); +}; diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm new file mode 100644 index 0000000000..c694a379b0 --- /dev/null +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm @@ -0,0 +1,118 @@ +/* + * 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 "ASStackTextPositionedLayout.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" + +static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, + const ASLayout *layout) { + + __weak id textChild = (id) layout.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + case ASStackTextLayoutBaselineAlignmentFirst: + return textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return layout.size.height + textChild.descender; + } + +} + +static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, + const ASLayout *l, + const CGFloat maxAscender, + const CGFloat maxBaseline) +{ + if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { + __weak id textChild = (id)l.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentFirst: + return maxAscender - textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return maxBaseline + textChild.descender - textChild.ascender; + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + } + } + return 0; +} + +static CGFloat maxDimensionForLayout(const ASLayout *l, + const ASStackLayoutSpecStyle &style) +{ + CGFloat maxDimension = crossDimension(style.direction, l.size); + style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.position.x : maxDimension += l.position.y; + return maxDimension; +} + +ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASStackTextLayoutSpecStyle &textStyle, + const ASSizeRange &constrainedSize) +{ + ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; + + + // Get the largest distance from the top of the stack to a baseline. This is the baseline we will align to. + const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return baselineForItem(textStyle, a) < baselineForItem(textStyle, b); + }); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); + + // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASStackTextLayoutSpec as its ascender. + 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; + }); + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + + CGPoint p = CGPointZero; + BOOL first = YES; + auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ + __weak id textChild = (id) l.layoutableObject; + p = p + directionPoint(stackStyle.direction, textChild.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); + } + first = NO; + + // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y + l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); + + // If we are a vertical stack, add the minDescender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes + CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); + + return l; + }); + + // The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below). + const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), + [&](ASLayout *a, ASLayout *b) { + return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); + }); + 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 CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); + + // find the child with the largest height. Use that child's descender as the descender to pass back to the ASStackTextLayoutSpec. + 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; + + return {stackedChildren, crossSize, maxAscender, minDescender}; +} From 6624b84c4c1b0d92b6f95383499ab940ef829890 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Thu, 20 Aug 2015 10:40:00 -0700 Subject: [PATCH 065/145] Static layout doesn't properly set a height for collection view cells because their size.width / size.height isn't nan, but FLT_MAX. --- AsyncDisplayKit/ASCollectionView.mm | 10 ++++++++-- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8223d5208e..54891897dd 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -560,10 +560,16 @@ static BOOL _isInterceptedSelector(SEL sel) if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { constrainedSize.min.width = MAX(0, constrainedSize.min.width - sectionInset.left - sectionInset.right); - constrainedSize.max.width = MAX(0, constrainedSize.max.width - sectionInset.left - sectionInset.right); + //ignore insets for FLT_MAX so FLT_MAX can be compared against + if (constrainedSize.max.width - FLT_EPSILON < FLT_MAX) { + constrainedSize.max.width = MAX(0, constrainedSize.max.width - sectionInset.left - sectionInset.right); + } } else { constrainedSize.min.height = MAX(0, constrainedSize.min.height - sectionInset.top - sectionInset.bottom); - constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom); + //ignore insets for FLT_MAX so FLT_MAX can be compared against + if (constrainedSize.max.height - FLT_EPSILON < FLT_MAX) { + constrainedSize.max.height = MAX(0, constrainedSize.max.height - sectionInset.top - sectionInset.bottom); + } } return constrainedSize; diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index daf0971e56..1610ab0db3 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -74,14 +74,14 @@ [sublayouts addObject:sublayout]; } - if (isnan(size.width)) { + if (isnan(size.width) || size.width >= FLT_MAX - FLT_EPSILON) { size.width = constrainedSize.min.width; for (ASLayout *sublayout in sublayouts) { size.width = MAX(size.width, sublayout.position.x + sublayout.size.width); } } - if (isnan(size.height)) { + if (isnan(size.height) || size.height >= FLT_MAX - FLT_EPSILON) { size.height = constrainedSize.min.height; for (ASLayout *sublayout in sublayouts) { size.height = MAX(size.height, sublayout.position.y + sublayout.size.height); From 36bac93a53172b53eb2b59624a2a239faca4f436 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Thu, 20 Aug 2015 10:55:15 -0700 Subject: [PATCH 066/145] 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 06b00c56b9..0b639f2a70 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 3e5d5c44cbfaff7a07377a91275626bfa92c707c Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 20 Aug 2015 22:06:46 +0300 Subject: [PATCH 067/145] Test relayout caused by a different constrained size for each and every node. All nodes, including loading ones, must be re-measured. --- AsyncDisplayKitTests/ASTableViewTests.m | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index e73ff71e33..4a23a65916 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -202,4 +202,44 @@ } } +- (void)testRelayoutAllRows +{ + // 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) + style:UITableViewStylePlain + asyncDataFetching:YES]; + CGSize tableViewFinalSize = CGSizeMake(100, 500); + + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + + tableView.asyncDelegate = dataSource; + tableView.asyncDataSource = dataSource; + + [tableView reloadData]; + + [tableView beginUpdates]; + + tableView.frame = CGRectMake(0, 0, tableViewFinalSize.width, tableViewFinalSize.height); + [tableView layoutIfNeeded]; + + XCTestExpectation *nodesMeasuredUsingNewConstrainedSizeExpectation = [self expectationWithDescription:@"nodesMeasuredUsingNewConstrainedSize"]; + + [tableView endUpdatesAnimated:NO completion:^(BOOL completed) { + 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); + } + } + [nodesMeasuredUsingNewConstrainedSizeExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:5 handler:^(NSError *error) { + if (error) { + XCTFail(@"Expectation failed: %@", error); + } + }]; +} + @end From 97f323a8d94863d1f8fbe09ec8f7d2a2d71a32da Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 20 Aug 2015 23:10:07 +0300 Subject: [PATCH 068/145] Fix relayoutAllRows to trigger measurement (with the new constrained size) on all nodes, including loading ones. --- AsyncDisplayKit/Details/ASDataController.mm | 29 ++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4cfc03270d..b0e7a94bb5 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -564,13 +564,30 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - ASSizeRange constrainedSize = [_dataSource dataController:self - constrainedSizeForNodeAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { + if (!nodes.count) { + return; + } + + [self accessDataSourceWithBlock:^{ + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; }]; + }; + + // Can't relayout right away because _completedNodes may not be up-to-date, + // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedNodes + // (see _layoutNodes:atIndexPaths:withAnimationOptions:). + [_editingTransactionQueue addOperationWithBlock:^{ + ASDisplayNodePerformBlockOnMainThread(^{ + relayoutNodesBlock(_completedNodes); + }); }]; }]; } From 0a398301b0b50d91d66b4226dd83addebf3f9d74 Mon Sep 17 00:00:00 2001 From: rcancro Date: Thu, 20 Aug 2015 20:43:18 -0700 Subject: [PATCH 069/145] fixed bug for baseline last --- AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm index c694a379b0..f9429ee177 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm @@ -39,7 +39,7 @@ static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, case ASStackTextLayoutBaselineAlignmentFirst: return maxAscender - textChild.ascender; case ASStackTextLayoutBaselineAlignmentLast: - return maxBaseline + textChild.descender - textChild.ascender; + return maxBaseline - baselineForItem(style, l); case ASStackTextLayoutBaselineAlignmentNone: return 0; } @@ -91,7 +91,7 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); - // If we are a vertical stack, add the minDescender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes + // If we are a vertical stack, add the item's descender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); From 641929c4e5cccc3a5cd92c33fab1a052bf6e6caf Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Fri, 21 Aug 2015 17:22:55 +0300 Subject: [PATCH 070/145] 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 6a9a3bd339..8948591ddb 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 a852bcb801..84e4b49e80 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 023ad89815b0920547b3775d66ab35880bfb3ab9 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Fri, 21 Aug 2015 08:54:28 -0700 Subject: [PATCH 071/145] Renamed ASStackText... to ASBaselineStack... --- AsyncDisplayKit.xcodeproj/project.pbxproj | 54 ++++++++++--------- AsyncDisplayKit/ASTextNode.h | 3 +- ...youtSpec.h => ASBaselineStackLayoutSpec.h} | 19 +++---- ...utSpec.mm => ASBaselineStackLayoutSpec.mm} | 20 +++---- .../Layout/ASBaselineStackLayoutable.h | 23 ++++++++ AsyncDisplayKit/Layout/ASStackLayoutable.h | 7 --- ...ut.h => ASBaselineStackPositionedLayout.h} | 8 +-- ....mm => ASBaselineStackPositionedLayout.mm} | 38 ++++++------- 8 files changed, 98 insertions(+), 74 deletions(-) rename AsyncDisplayKit/Layout/{ASStackTextLayoutSpec.h => ASBaselineStackLayoutSpec.h} (70%) rename AsyncDisplayKit/Layout/{ASStackTextLayoutSpec.mm => ASBaselineStackLayoutSpec.mm} (71%) create mode 100644 AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h rename AsyncDisplayKit/Private/{ASStackTextPositionedLayout.h => ASBaselineStackPositionedLayout.h} (72%) rename AsyncDisplayKit/Private/{ASStackTextPositionedLayout.mm => ASBaselineStackPositionedLayout.mm} (76%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 83427c715c..97b20c3f2b 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,14 +217,16 @@ 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 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; - 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; - 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; - 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; + 9C204A641B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; + 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; + 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */; }; + 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; @@ -566,10 +568,11 @@ 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 /* ASStackTextPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackTextPositionedLayout.h; sourceTree = ""; }; - 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackTextPositionedLayout.mm; sourceTree = ""; }; - 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; - 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; + 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselineStackPositionedLayout.h; sourceTree = ""; }; + 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselineStackPositionedLayout.mm; sourceTree = ""; }; + 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h; sourceTree = ""; }; + 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h; sourceTree = ""; }; + 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineStackLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -936,8 +939,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, - 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */, - 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */, + 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -960,6 +963,7 @@ children = ( ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, + 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, @@ -983,8 +987,8 @@ ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, - 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */, - 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1045,6 +1049,7 @@ 464052201A3F83C40061C0BA /* ASDataController.h in Headers */, 05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, + 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */, 058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */, 058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1066,7 +1071,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, - 9C204A641B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, + 9C204A641B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1093,7 +1098,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, - 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, + 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1159,7 +1164,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, - 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, + 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1169,7 +1174,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, - 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, + 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1213,6 +1218,7 @@ 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, + 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */, 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */, B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1435,7 +1441,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, - 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, + 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1474,7 +1480,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, - 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, + 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1536,10 +1542,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, + 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, - 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, + 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 681c2cb71e..d47925ed78 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,6 +7,7 @@ */ #import +#import @protocol ASTextNodeDelegate; @@ -30,7 +31,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/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h similarity index 70% rename from AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h rename to AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h index a7b94da03b..df613f42fc 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h @@ -9,15 +9,16 @@ */ #import +#import -typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { +typedef NS_ENUM(NSUInteger, ASBaselineStackLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ - ASStackTextLayoutBaselineAlignmentNone, + ASBaselineStackLayoutBaselineAlignmentNone, /** Align all children to the first baseline. This is only valid for a horizontal stack */ - ASStackTextLayoutBaselineAlignmentFirst, + ASBaselineStackLayoutBaselineAlignmentFirst, /** 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 */ - ASStackTextLayoutBaselineAlignmentLast, + ASBaselineStackLayoutBaselineAlignmentLast, }; @@ -26,24 +27,24 @@ typedef struct { ASStackLayoutSpecStyle stackLayoutStyle; /** The type of baseline alignment */ - ASStackTextLayoutBaselineAlignment baselineAlignment; -} ASStackTextLayoutSpecStyle; + ASBaselineStackLayoutBaselineAlignment baselineAlignment; +} ASBaselineStackLayoutSpecStyle; /** A specialized version of a stack layout that aligns its children on a baseline. This spec only works with - ASStackTextLayoutable children. + ASBaselineStackLayoutable 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 ASStackTextLayoutSpec : ASLayoutSpec +@interface ASBaselineStackLayoutSpec : ASLayoutSpec /** @param style Specifies how children are laid out. @param children ASTextLayoutable children to be positioned. */ -+ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; ++ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children; @end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm similarity index 71% rename from AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm rename to AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm index 67c6942696..52d6be3870 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm @@ -8,7 +8,7 @@ * */ -#import "ASStackTextLayoutSpec.h" +#import "ASBaselineStackLayoutSpec.h" #import "ASStackLayoutable.h" #import @@ -21,13 +21,13 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" -#import "ASStackTextPositionedLayout.h" +#import "ASBaselineStackPositionedLayout.h" #import "ASThread.h" -@implementation ASStackTextLayoutSpec +@implementation ASBaselineStackLayoutSpec { - ASStackTextLayoutSpecStyle _textStyle; + ASBaselineStackLayoutSpecStyle _textStyle; std::vector> _stackChildren; ASDN::RecursiveMutex _propertyLock; } @@ -35,16 +35,16 @@ @synthesize ascender = _ascender; @synthesize descender = _descender; -+ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children ++ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children { - ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASBaselineStackLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); - ASStackTextLayoutSpec *spec = [super new]; + ASBaselineStackLayoutSpec *spec = [super new]; if (spec) { spec->_textStyle = style; spec->_stackChildren = std::vector>(); - for (id child in children) { - ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineStackLayoutable)], @"child must conform to ASStackLayoutable"); spec->_stackChildren.push_back(child); } @@ -63,7 +63,7 @@ const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - const auto baselinePositionedLayout = ASStackTextPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h new file mode 100644 index 0000000000..e061641c79 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h @@ -0,0 +1,23 @@ +// +// ASBaselineStackLayoutable.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/21/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASStackLayoutable.h" + +@protocol ASBaselineStackLayoutable + +/** + * @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/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 057ca8b0fd..b23b4fbbdb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -50,10 +50,3 @@ @property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; @end - -@protocol ASStackTextLayoutable - -@property (nonatomic, readwrite) CGFloat ascender; -@property (nonatomic, readwrite) CGFloat descender; - -@end diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h similarity index 72% rename from AsyncDisplayKit/Private/ASStackTextPositionedLayout.h rename to AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h index 9794e9b807..877da2062f 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h @@ -10,17 +10,17 @@ #import "ASLayout.h" #import "ASDimension.h" -#import "ASStackTextLayoutSpec.h" +#import "ASBaselineStackLayoutSpec.h" #import "ASStackPositionedLayout.h" -struct ASStackTextPositionedLayout { +struct ASBaselineStackPositionedLayout { 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 ASStackTextPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASStackTextLayoutSpecStyle &textStyle, + static ASBaselineStackPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineStackLayoutSpecStyle &textStyle, const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm similarity index 76% rename from AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm rename to AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm index f9429ee177..42ab80d339 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm @@ -8,39 +8,39 @@ * */ -#import "ASStackTextPositionedLayout.h" +#import "ASBaselineStackPositionedLayout.h" #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" -static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, +static CGFloat baselineForItem(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *layout) { - __weak id textChild = (id) layout.layoutableObject; + __weak id textChild = (id) layout.layoutableObject; switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentNone: + case ASBaselineStackLayoutBaselineAlignmentNone: return 0; - case ASStackTextLayoutBaselineAlignmentFirst: + case ASBaselineStackLayoutBaselineAlignmentFirst: return textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: + case ASBaselineStackLayoutBaselineAlignmentLast: return layout.size.height + textChild.descender; } } -static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, +static CGFloat baselineOffset(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *l, const CGFloat maxAscender, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id textChild = (id)l.layoutableObject; + __weak id textChild = (id)l.layoutableObject; switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentFirst: + case ASBaselineStackLayoutBaselineAlignmentFirst: return maxAscender - textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: + case ASBaselineStackLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); - case ASStackTextLayoutBaselineAlignmentNone: + case ASBaselineStackLayoutBaselineAlignmentNone: return 0; } } @@ -55,8 +55,8 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, return maxDimension; } -ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASStackTextLayoutSpecStyle &textStyle, +ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineStackLayoutSpecStyle &textStyle, const ASSizeRange &constrainedSize) { ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; @@ -68,16 +68,16 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo }); const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); - // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASStackTextLayoutSpec as its ascender. + // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASBaselineStackLayoutSpec as its ascender. 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; + return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; + __weak id textChild = (id) l.layoutableObject; p = p + directionPoint(stackStyle.direction, textChild.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point @@ -108,11 +108,11 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // find the child with the largest height. Use that child's descender as the descender to pass back to the ASStackTextLayoutSpec. + // find the child with the largest height. Use that child's descender as the descender to pass back to the ASBaselineStackLayoutSpec. 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 : ((id)(*descenderIt).layoutableObject).descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } From 608f62d4872a00533f47d0e042f27dd5532c05c2 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 21 Aug 2015 10:08:15 -0700 Subject: [PATCH 072/145] fixed copyright --- AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h index e061641c79..0911b93908 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h @@ -1,10 +1,10 @@ -// -// ASBaselineStackLayoutable.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/21/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 "ASStackLayoutable.h" From c06a6be188e1730382fb4cfd5248d17da22214b2 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 21 Aug 2015 16:02:36 -0700 Subject: [PATCH 073/145] ASLayoutSpec are temporarily mutable and have a more obj-c interface --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 +- AsyncDisplayKit/ASCellNode.m | 2 +- AsyncDisplayKit/ASDisplayNode.mm | 7 +- .../Layout/ASBackgroundLayoutSpec.h | 5 +- .../Layout/ASBackgroundLayoutSpec.mm | 33 ++++-- AsyncDisplayKit/Layout/ASCenterLayoutSpec.h | 10 +- AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 45 ++++++-- AsyncDisplayKit/Layout/ASInsetLayoutSpec.h | 5 +- AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 34 ++++-- AsyncDisplayKit/Layout/ASLayout.h | 16 +-- AsyncDisplayKit/Layout/ASLayout.mm | 30 ++--- AsyncDisplayKit/Layout/ASLayoutSpec.h | 11 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 13 ++- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h | 5 +- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 30 +++-- AsyncDisplayKit/Layout/ASRatioLayoutSpec.h | 6 +- AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 39 ++++--- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 31 ++++-- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 105 ++++++++++++++---- AsyncDisplayKit/Layout/ASStaticLayoutSpec.h | 8 +- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 36 +++--- .../Private/ASStackLayoutSpecUtilities.h | 7 ++ .../Private/ASStackUnpositionedLayout.h | 1 + .../Private/ASStackUnpositionedLayout.mm | 2 +- .../Kittens/Sample.xcodeproj/project.pbxproj | 2 + examples/Kittens/Sample/BlurbNode.m | 16 +-- examples/Kittens/Sample/KittenNode.mm | 20 ++-- 27 files changed, 357 insertions(+), 168 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index aee3b1867a..3203623ff8 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -447,13 +447,13 @@ 058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Subclasses.h"; sourceTree = ""; }; 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDisplayNode.mm; sourceTree = ""; }; - 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; }; 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeExtras.h; sourceTree = ""; }; 058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeExtras.mm; sourceTree = ""; }; 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 = ""; }; @@ -687,7 +687,9 @@ 058D09AD195D04C000B7D73C /* Products */, FD40E2760492F0CAAEAD552D /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 058D09AD195D04C000B7D73C /* Products */ = { isa = PBXGroup; diff --git a/AsyncDisplayKit/ASCellNode.m b/AsyncDisplayKit/ASCellNode.m index 4273a10cc1..14b304086b 100644 --- a/AsyncDisplayKit/ASCellNode.m +++ b/AsyncDisplayKit/ASCellNode.m @@ -111,7 +111,7 @@ static const CGFloat kFontSize = 18.0f; static const CGFloat kHorizontalPadding = 15.0f; static const CGFloat kVerticalPadding = 11.0f; UIEdgeInsets insets = UIEdgeInsetsMake(kVerticalPadding, kHorizontalPadding, kVerticalPadding, kHorizontalPadding); - return [ASInsetLayoutSpec newWithInsets:insets child:_textNode]; + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_textNode]; } - (void)setText:(NSString *)text diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c02c053e25..ab1a644091 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1333,12 +1333,13 @@ static NSInteger incrementIfFound(NSInteger i) { { ASDisplayNodeAssertThreadAffinity(self); if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) { - id layoutSpec = [self layoutSpecThatFits:constrainedSize]; + ASLayoutSpec *layoutSpec = [self layoutSpecThatFits:constrainedSize]; + layoutSpec.isMutable = NO; ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize]; // Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct. if (layout.layoutableObject != self) { layout.position = CGPointZero; - layout = [ASLayout newWithLayoutableObject:self size:layout.size sublayouts:@[layout]]; + layout = [ASLayout layoutWithLayoutableObject:self size:layout.size sublayouts:@[layout]]; } return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { return [_subnodes containsObject:evaluatedLayout.layoutableObject]; @@ -1347,7 +1348,7 @@ static NSInteger incrementIfFound(NSInteger i) { // If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used, // assume that the default implementation of -calculateSizeThatFits: returns it. CGSize size = [self calculateSizeThatFits:constrainedSize.max]; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; + return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; } } diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h index 508f7c8b28..ecc2e48473 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h @@ -15,11 +15,14 @@ */ @interface ASBackgroundLayoutSpec : ASLayoutSpec +@property (nonatomic, strong) id child; +@property (nonatomic, strong) id background; + /** @param child A child that is laid out to determine the size of this spec. If this is nil, then this method returns nil. @param background A layoutable object that is laid out behind the child. May be nil, in which case the background is omitted. */ -+ (instancetype)newWithChild:(id)child background:(id)background; ++ (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background; @end diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index ae03e78836..e23ebaf903 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -23,20 +23,21 @@ @implementation ASBackgroundLayoutSpec -+ (instancetype)newWithChild:(id)child background:(id)background +- (instancetype)initWithChild:(id)child background:(id)background { - if (child == nil) { - return nil; + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _child = child; + _background = background; } - ASBackgroundLayoutSpec *spec = [super new]; - spec->_child = child; - spec->_background = background; - return spec; + return self; } -+ (instancetype)new + ++ (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background; { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + return [[self alloc] initWithChild:child background:background]; } /** @@ -56,7 +57,19 @@ contentsLayout.position = CGPointZero; [sublayouts addObject:contentsLayout]; - return [ASLayout newWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts]; + return [ASLayout layoutWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts]; +} + +- (void)setBackground:(id)background +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _background = background; +} + +- (void)setChild:(id)child +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _child = child; } @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index 84e015ac57..dc50b265e7 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -37,6 +37,10 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { /** Lays out a single layoutable child and position it so that it is centered into the layout bounds. */ @interface ASCenterLayoutSpec : ASLayoutSpec +@property (nonatomic, assign) ASCenterLayoutSpecCenteringOptions centeringOptions; +@property (nonatomic, assign) ASCenterLayoutSpecSizingOptions sizingOptions; +@property (nonatomic, strong) id child; + /** * Initializer. * @@ -46,8 +50,8 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { * * @param child The child to center. */ -+ (instancetype)newWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions - sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions - child:(id)child; ++ (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child; @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index f96b359172..fd52637480 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -20,22 +20,43 @@ id _child; } -+ (instancetype)newWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions - sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions - child:(id)child +- (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child; { - ASCenterLayoutSpec *spec = [super new]; - if (spec) { - spec->_centeringOptions = centeringOptions; - spec->_sizingOptions = sizingOptions; - spec->_child = child; + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _centeringOptions = centeringOptions; + _sizingOptions = sizingOptions; + _child = child; } - return spec; + return self; } -+ (instancetype)new ++ (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions + sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions + child:(id)child { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + 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"); + _centeringOptions = centeringOptions; +} + +- (void)setSizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _sizingOptions = sizingOptions; } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize @@ -73,7 +94,7 @@ ASRoundPixelValue(shouldCenterAlongY ? (size.height - sublayout.size.height) * 0.5f : 0) }; - return [ASLayout newWithLayoutableObject:self size:size sublayouts:@[sublayout]]; + return [ASLayout layoutWithLayoutableObject:self size:size sublayouts:@[sublayout]]; } @end diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h index 7f1b0c9c00..3b140dfcc7 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h @@ -29,10 +29,13 @@ */ @interface ASInsetLayoutSpec : ASLayoutSpec +@property (nonatomic, strong) id child; +@property (nonatomic, assign) UIEdgeInsets insets; + /** @param insets The amount of space to inset on each side. @param child The wrapped child to inset. If nil, this method returns nil. */ -+ (instancetype)newWithInsets:(UIEdgeInsets)insets child:(id)child; ++ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id)child; @end diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 8db3975773..01c382b663 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -43,22 +43,32 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) @implementation ASInsetLayoutSpec -+ (instancetype)newWithInsets:(UIEdgeInsets)insets child:(id)child +- (instancetype)initWithInsets:(UIEdgeInsets)insets child:(id)child; { - if (child == nil) { - return nil; + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _insets = insets; + _child = child; } - ASInsetLayoutSpec *spec = [super new]; - if (spec) { - spec->_insets = insets; - spec->_child = child; - } - return spec; + return self; } -+ (instancetype)new ++ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id)child { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + 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"); + _insets = insets; } /** @@ -103,7 +113,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) sublayout.position = CGPointMake(x, y); - return [ASLayout newWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]]; + return [ASLayout layoutWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]]; } @end diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index a3d32feddc..71892b5996 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -43,10 +43,10 @@ extern BOOL CGPointIsNull(CGPoint point); * * @param sublayouts Sublayouts belong to the new layout. */ -+ (instancetype)newWithLayoutableObject:(id)layoutableObject - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts; ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject + size:(CGSize)size + position:(CGPoint)position + sublayouts:(NSArray *)sublayouts; /** * Convenience initializer that has CGPointNull position. @@ -60,9 +60,9 @@ extern BOOL CGPointIsNull(CGPoint point); * * @param sublayouts Sublayouts belong to the new layout. */ -+ (instancetype)newWithLayoutableObject:(id)layoutableObject - size:(CGSize)size - sublayouts:(NSArray *)sublayouts; ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject + size:(CGSize)size + sublayouts:(NSArray *)sublayouts; /** * Convenience that has CGPointNull position and no sublayouts. @@ -73,7 +73,7 @@ extern BOOL CGPointIsNull(CGPoint point); * * @param size The size of this layout. */ -+ (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size; ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject size:(CGSize)size; /** diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 31dd8ebc27..6695a07bde 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -22,10 +22,10 @@ extern BOOL CGPointIsNull(CGPoint point) @implementation ASLayout -+ (instancetype)newWithLayoutableObject:(id)layoutableObject - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject + size:(CGSize)size + position:(CGPoint)position + sublayouts:(NSArray *)sublayouts { ASDisplayNodeAssert(layoutableObject, @"layoutableObject is required."); for (ASLayout *sublayout in sublayouts) { @@ -42,16 +42,16 @@ extern BOOL CGPointIsNull(CGPoint point) return l; } -+ (instancetype)newWithLayoutableObject:(id)layoutableObject - size:(CGSize)size - sublayouts:(NSArray *)sublayouts ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject + size:(CGSize)size + sublayouts:(NSArray *)sublayouts { - return [self newWithLayoutableObject:layoutableObject size:size position:CGPointNull sublayouts:sublayouts]; + return [self layoutWithLayoutableObject:layoutableObject size:size position:CGPointNull sublayouts:sublayouts]; } -+ (instancetype)newWithLayoutableObject:(id)layoutableObject size:(CGSize)size ++ (instancetype)layoutWithLayoutableObject:(id)layoutableObject size:(CGSize)size { - return [self newWithLayoutableObject:layoutableObject size:size sublayouts:nil]; + return [self layoutWithLayoutableObject:layoutableObject size:size sublayouts:nil]; } - (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicateBlock @@ -76,10 +76,10 @@ extern BOOL CGPointIsNull(CGPoint point) context.visited = YES; if (predicateBlock(context.layout)) { - [flattenedSublayouts addObject:[ASLayout newWithLayoutableObject:context.layout.layoutableObject - size:context.layout.size - position:context.absolutePosition - sublayouts:nil]]; + [flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:context.layout.layoutableObject + size:context.layout.size + position:context.absolutePosition + sublayouts:nil]]; } for (ASLayout *sublayout in context.layout.sublayouts) { @@ -88,7 +88,7 @@ extern BOOL CGPointIsNull(CGPoint point) } } - return [ASLayout newWithLayoutableObject:_layoutableObject size:_size sublayouts:flattenedSublayouts]; + return [ASLayout layoutWithLayoutableObject:_layoutableObject size:_size sublayouts:flattenedSublayouts]; } @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index c2602824e7..8edb8d8477 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -10,7 +10,16 @@ #import -/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ +/** A layout spec is an temporarly mutable object that describes a layout, loosely inspired by React. */ @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. + */ +@property (nonatomic, assign) BOOL isMutable; + +- (instancetype)init; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 8b3efd6f4d..6d6ab903f4 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -25,20 +25,21 @@ @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; -+ (instancetype)new +- (instancetype)init { - ASLayoutSpec *spec = [super new]; - if (spec) { - spec->_flexBasis = ASRelativeDimensionUnconstrained; + self = [super init]; + if (self) { + _flexBasis = ASRelativeDimensionUnconstrained; + _isMutable = YES; } - return spec; + return self; } #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - return [ASLayout newWithLayoutableObject:self size:constrainedSize.min]; + return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h index 846b0cece5..ec82fe00e6 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h @@ -15,6 +15,9 @@ */ @interface ASOverlayLayoutSpec : ASLayoutSpec -+ (instancetype)newWithChild:(id)child overlay:(id)overlay; +@property (nonatomic, strong) id child; +@property (nonatomic, strong) id overlay; + ++ (instancetype)overlayLayoutWithChild:(id)child overlay:(id)overlay; @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index b478f69971..c9d0edb80c 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -20,20 +20,32 @@ id _child; } -+ (instancetype)newWithChild:(id)child overlay:(id)overlay +- (instancetype)initWithChild:(id)child overlay:(id)overlay { - ASOverlayLayoutSpec *spec = [super new]; - if (spec) { + self = [super init]; + if (self) { ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - spec->_overlay = overlay; - spec->_child = child; + _overlay = overlay; + _child = child; } - return spec; + return self; } -+ (instancetype)new ++ (instancetype)overlayLayoutWithChild:(id)child overlay:(id)overlay { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + 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; } /** @@ -50,7 +62,7 @@ [sublayouts addObject:overlayLayout]; } - return [ASLayout newWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts]; + return [ASLayout layoutWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts]; } @end diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h index 73be620d71..fd7f6d657b 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h @@ -31,6 +31,10 @@ **/ @interface ASRatioLayoutSpec : ASLayoutSpec -+ (instancetype)newWithRatio:(CGFloat)ratio child:(id)child; + +@property (nonatomic, strong) id child; +@property (nonatomic, assign) CGFloat ratio; + ++ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child; @end diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 2e358b533f..c106844c44 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -25,24 +25,33 @@ id _child; } -+ (instancetype)newWithRatio:(CGFloat)ratio child:(id)child ++ (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child { - ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); - if (child == nil) { - return nil; - } - - ASRatioLayoutSpec *spec = [super new]; - if (spec) { - spec->_ratio = ratio; - spec->_child = child; - } - return spec; + return [[self alloc] initWithRatio:ratio child:child]; } -+ (instancetype)new +- (instancetype)initWithRatio:(CGFloat)ratio child:(id)child; { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + self = [super init]; + if (self) { + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); + _ratio = ratio; + _child = 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"); + _ratio = ratio; } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize @@ -70,7 +79,7 @@ const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize); ASLayout *sublayout = [_child measureWithSizeRange:childRange]; sublayout.position = CGPointZero; - return [ASLayout newWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; + return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; } @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 29f20603c5..c39522a83f 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -49,16 +49,6 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { ASStackLayoutAlignItemsStretch, }; -typedef struct { - /** Specifies the direction children are stacked in. */ - ASStackLayoutDirection direction; - /** The amount of space between each child. */ - CGFloat spacing; - /** How children are aligned if there are no flexible children. */ - ASStackLayoutJustifyContent justifyContent; - /** Orientation of children along cross axis */ - ASStackLayoutAlignItems alignItems; -} ASStackLayoutSpecStyle; /** A simple layout spec that stacks a list of children vertically or horizontally. @@ -83,10 +73,29 @@ typedef struct { */ @interface ASStackLayoutSpec : 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; + +- (instancetype)init; + /** @param style Specifies how children are laid out. @param children ASLayoutable children to be positioned. */ -+ (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children; ++ (instancetype)satckLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + contentJustification:(ASStackLayoutJustifyContent)justifyContent + itemAlignment:(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 75b699c55b..e712bd2d0b 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -23,37 +23,104 @@ @implementation ASStackLayoutSpec { - ASStackLayoutSpecStyle _style; std::vector> _children; } -+ (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children +- (instancetype)init { - ASStackLayoutSpec *spec = [super new]; - if (spec) { - spec->_style = style; - spec->_children = std::vector>(); - for (id child in children) { - spec->_children.push_back(child); - } - } - return spec; + return [self initWithDirection:ASStackLayoutDirectionHorizontal + spacing:0.0 + contentJustification:ASStackLayoutJustifyContentStart + itemAlignment:ASStackLayoutAlignItemsStart + children:nil]; } -+ (instancetype)new ++ (instancetype)satckLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + contentJustification:(ASStackLayoutJustifyContent)justifyContent + itemAlignment:(ASStackLayoutAlignItems)alignItems + children:(NSArray *)children { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + return [[self alloc] initWithDirection:direction + spacing:spacing + contentJustification:justifyContent + itemAlignment:alignItems + children:children]; +} + +- (instancetype)initWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + contentJustification:(ASStackLayoutJustifyContent)justifyContent + itemAlignment:(ASStackLayoutAlignItems)alignItems + children:(NSArray *)children; +{ + self = [super init]; + if (self) { + _direction = direction; + _alignItems = alignItems; + _spacing = spacing; + _justifyContent = justifyContent; + + _children = std::vector>(); + for (id child in children) { + _children.push_back(child); + } + } + 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"); + _direction = direction; +} + +- (void)setAlignItems:(ASStackLayoutAlignItems)alignItems +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _alignItems = alignItems; +} + +- (void)setJustifyContent:(ASStackLayoutJustifyContent)justifyContent +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _justifyContent = justifyContent; +} + +- (void)setSpacing:(CGFloat)spacing +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _spacing = spacing; } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, _style, constrainedSize); - const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, _style, constrainedSize); - const CGSize finalSize = directionSize(_style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); + ASStackLayoutSpecStyle style = {.direction = _direction, + .spacing = _spacing, + .justifyContent = _justifyContent, + .alignItems = _alignItems + }; + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, 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()]; - return [ASLayout newWithLayoutableObject:self - size:ASSizeRangeClamp(constrainedSize, finalSize) - sublayouts:sublayouts]; + return [ASLayout layoutWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; } @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 259c32c4d6..38ed2f06d6 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -34,7 +34,7 @@ * * @param size The size range that this child's size is trstricted according to. */ -+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; ++ (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 @@ -44,7 +44,7 @@ * * @param layoutableObject The backing ASLayoutable object of this child. */ -+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; ++ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; @end @@ -58,6 +58,8 @@ /** @param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild. */ -+ (instancetype)newWithChildren:(NSArray *)children; ++ (instancetype)staticLayoutSpecWithChildren:(NSArray *)children; + +- (void)addChild:(ASStaticLayoutSpecChild *)child; @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index daf0971e56..b0becb0093 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -16,9 +16,9 @@ @implementation ASStaticLayoutSpecChild -+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size ++ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; { - ASStaticLayoutSpecChild *c = [super new]; + ASStaticLayoutSpecChild *c = [[super alloc] init]; if (c) { c->_position = position; c->_layoutableObject = layoutableObject; @@ -27,9 +27,9 @@ return c; } -+ (instancetype)newWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject ++ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject { - return [self newWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained]; + return [self staticLayoutChildWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained]; } @end @@ -39,18 +39,24 @@ NSArray *_children; } -+ (instancetype)newWithChildren:(NSArray *)children ++ (instancetype)staticLayoutSpecWithChildren:(NSArray *)children { - ASStaticLayoutSpec *spec = [super new]; - if (spec) { - spec->_children = children; - } - return spec; + return [[self alloc] initWithChildren:children]; } -+ (instancetype)new +- (instancetype)initWithChildren:(NSArray *)children { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + self = [super init]; + if (self) { + _children = children; + } + return self; +} + +- (void)addChild:(ASStaticLayoutSpecChild *)child +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + _children = [_children arrayByAddingObject:child]; } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize @@ -88,9 +94,9 @@ } } - return [ASLayout newWithLayoutableObject:self - size:ASSizeRangeClamp(constrainedSize, size) - sublayouts:sublayouts]; + return [ASLayout layoutWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, size) + sublayouts:sublayouts]; } @end diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 4f5eff0aeb..33895b9bd5 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -10,6 +10,13 @@ #import "ASStackLayoutSpec.h" +typedef struct { + ASStackLayoutDirection direction; + CGFloat spacing; + ASStackLayoutJustifyContent justifyContent; + ASStackLayoutAlignItems alignItems; +} ASStackLayoutSpecStyle; + inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size) { return (direction == ASStackLayoutDirectionVertical) ? size.height : size.width; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 45f648192e..4112af8e66 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -11,6 +11,7 @@ #import #import "ASLayout.h" +#import "ASStackLayoutSpecUtilities.h" #import "ASStackLayoutSpec.h" struct ASStackUnpositionedItem { diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 9eb83a5b0d..6e275c82c0 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -297,7 +297,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { - return { child, [ASLayout newWithLayoutableObject:child size:{0, 0}] }; + return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; } else { return { child, diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 293b513d1e..1e08bbe8c1 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -58,7 +58,9 @@ 1A943BF0259746F18D6E423F /* Frameworks */, 1AE410B73DA5C3BD087ACDD7 /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 05E2128219D4DB510098F589 /* Products */ = { isa = PBXGroup; diff --git a/examples/Kittens/Sample/BlurbNode.m b/examples/Kittens/Sample/BlurbNode.m index 1c646a25ad..a4802daf5c 100644 --- a/examples/Kittens/Sample/BlurbNode.m +++ b/examples/Kittens/Sample/BlurbNode.m @@ -76,14 +76,14 @@ static NSString *kLinkAttributeName = @"PlaceKittenNodeLinkAttributeName"; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - return - [ASInsetLayoutSpec - newWithInsets:UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding) - child: - [ASCenterLayoutSpec - newWithCenteringOptions:ASCenterLayoutSpecCenteringX // Center the text horizontally - sizingOptions:ASCenterLayoutSpecSizingOptionMinimumY // Takes up minimum height - child:_textNode]]; + ASCenterLayoutSpec *centerSpec = [[ASCenterLayoutSpec alloc] init]; + centerSpec.centeringOptions = ASCenterLayoutSpecCenteringX; + centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionMinimumY; + centerSpec.child = _textNode; + + + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding) + child:centerSpec]; } #else - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index a852bcb801..faf2cc9693 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -137,16 +137,16 @@ static const CGFloat kInnerPadding = 10.0f; _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); _textNode.flexShrink = YES; - return - [ASInsetLayoutSpec - newWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding) - child: - [ASStackLayoutSpec - newWithStyle:{ - .direction = ASStackLayoutDirectionHorizontal, - .spacing = kInnerPadding - } - children:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]]; + ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; + stackSpec.direction = ASStackLayoutDirectionHorizontal; + stackSpec.spacing = kInnerPadding; + [stackSpec addChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; + + ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; + insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding); + insetSpec.child = stackSpec; + + return insetSpec; } // With box model, you don't need to override this method, unless you want to add custom logic. From 680305704a57be924813a7810daef718cabbb33c Mon Sep 17 00:00:00 2001 From: rcancro Date: Fri, 21 Aug 2015 21:03:40 -0700 Subject: [PATCH 074/145] addressed comments and fixed tests --- .../Layout/ASBackgroundLayoutSpec.mm | 11 ++-- AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 12 ++--- AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 10 ++-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 8 +-- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h | 2 +- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 12 ++--- AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 12 ++--- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 12 ++--- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 53 ++++++------------- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 6 +-- .../ASCenterLayoutSpecSnapshotTests.mm | 8 +-- .../ASInsetLayoutSpecSnapshotTests.mm | 6 +-- .../ASLayoutSpecSnapshotTestsHelper.m | 2 +- .../ASOverlayLayoutSpecSnapshotTests.mm | 4 +- .../ASRatioLayoutSpecSnapshotTests.mm | 2 +- .../ASStackLayoutSpecSnapshotTests.mm | 30 ++++------- examples/Kittens/Sample/BlurbNode.m | 5 +- 17 files changed, 82 insertions(+), 113 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index e23ebaf903..fd3cfce134 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -25,12 +25,13 @@ - (instancetype)initWithChild:(id)child background:(id)background { - self = [super init]; - if (self) { - ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _child = child; - _background = background; + if (!(self = [super init])) { + return nil; } + + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _child = child; + _background = background; return self; } diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index fd52637480..1830895320 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -24,13 +24,13 @@ sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions child:(id)child; { - self = [super init]; - if (self) { - ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _centeringOptions = centeringOptions; - _sizingOptions = sizingOptions; - _child = child; + if (!(self = [super init])) { + return nil; } + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _centeringOptions = centeringOptions; + _sizingOptions = sizingOptions; + _child = child; return self; } diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 01c382b663..5ca96ab848 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -45,12 +45,12 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) - (instancetype)initWithInsets:(UIEdgeInsets)insets child:(id)child; { - self = [super init]; - if (self) { - ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _insets = insets; - _child = child; + if (!(self = [super init])) { + return nil; } + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + _insets = insets; + _child = child; return self; } diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 6d6ab903f4..ef09aa365e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -27,11 +27,11 @@ - (instancetype)init { - self = [super init]; - if (self) { - _flexBasis = ASRelativeDimensionUnconstrained; - _isMutable = YES; + if (!(self = [super init])) { + return nil; } + _flexBasis = ASRelativeDimensionUnconstrained; + _isMutable = YES; return self; } diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h index ec82fe00e6..35a9577dde 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h @@ -18,6 +18,6 @@ @property (nonatomic, strong) id child; @property (nonatomic, strong) id overlay; -+ (instancetype)overlayLayoutWithChild:(id)child overlay:(id)overlay; ++ (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay; @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index c9d0edb80c..f4f025573b 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -22,16 +22,16 @@ - (instancetype)initWithChild:(id)child overlay:(id)overlay { - self = [super init]; - if (self) { - ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - _overlay = overlay; - _child = child; + if (!(self = [super init])) { + return nil; } + ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); + _overlay = overlay; + _child = child; return self; } -+ (instancetype)overlayLayoutWithChild:(id)child overlay:(id)overlay ++ (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay { return [[self alloc] initWithChild:child overlay:overlay]; } diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index c106844c44..5e5c6f0c66 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -32,13 +32,13 @@ - (instancetype)initWithRatio:(CGFloat)ratio child:(id)child; { - self = [super init]; - if (self) { - ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); - _ratio = ratio; - _child = child; + if (!(self = [super init])) { + return nil; } + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); + _ratio = ratio; + _child = child; return self; } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index c39522a83f..e71de22819 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -85,15 +85,13 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { - (instancetype)init; /** - @param style Specifies how children are laid out. + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis @param children ASLayoutable children to be positioned. */ -+ (instancetype)satckLayoutSpecWithDirection:(ASStackLayoutDirection)direction - spacing:(CGFloat)spacing - contentJustification:(ASStackLayoutJustifyContent)justifyContent - itemAlignment:(ASStackLayoutAlignItems)alignItems - children:(NSArray *)children; - ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children; - (void)addChild:(id)child; - (void)addChildren:(NSArray *)children; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index e712bd2d0b..9d02cecb8a 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -28,43 +28,27 @@ - (instancetype)init { - return [self initWithDirection:ASStackLayoutDirectionHorizontal - spacing:0.0 - contentJustification:ASStackLayoutJustifyContentStart - itemAlignment:ASStackLayoutAlignItemsStart - children:nil]; + return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:nil]; } -+ (instancetype)satckLayoutSpecWithDirection:(ASStackLayoutDirection)direction - spacing:(CGFloat)spacing - contentJustification:(ASStackLayoutJustifyContent)justifyContent - itemAlignment:(ASStackLayoutAlignItems)alignItems - children:(NSArray *)children ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { - return [[self alloc] initWithDirection:direction - spacing:spacing - contentJustification:justifyContent - itemAlignment:alignItems - children:children]; + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems children:children]; } -- (instancetype)initWithDirection:(ASStackLayoutDirection)direction - spacing:(CGFloat)spacing - contentJustification:(ASStackLayoutJustifyContent)justifyContent - itemAlignment:(ASStackLayoutAlignItems)alignItems - children:(NSArray *)children; +- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { - self = [super init]; - if (self) { - _direction = direction; - _alignItems = alignItems; - _spacing = spacing; - _justifyContent = justifyContent; - - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + if (!(self = [super init])) { + return nil; + } + _direction = direction; + _alignItems = alignItems; + _spacing = spacing; + _justifyContent = justifyContent; + + _children = std::vector>(); + for (id child in children) { + _children.push_back(child); } return self; } @@ -82,7 +66,6 @@ } } - - (void)setDirection:(ASStackLayoutDirection)direction { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -109,11 +92,7 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle style = {.direction = _direction, - .spacing = _spacing, - .justifyContent = _justifyContent, - .alignItems = _alignItems - }; + ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index b0becb0093..cf2647eba8 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -46,10 +46,10 @@ - (instancetype)initWithChildren:(NSArray *)children { - self = [super init]; - if (self) { - _children = children; + if (!(self = [super init])) { + return nil; } + _children = children; return self; } diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 388e7d0e7e..5a7cbd0a92 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -52,9 +52,9 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}}; ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild: + backgroundLayoutSpecWithChild: [ASCenterLayoutSpec - newWithCenteringOptions:options + centerLayoutSpecWithCenteringOptions:options sizingOptions:sizingOptions child:foregroundNode] background:backgroundNode]; @@ -98,11 +98,11 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec - newWithCenteringOptions:ASCenterLayoutSpecCenteringNone + centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:{} child: [ASBackgroundLayoutSpec - newWithChild:[ASStackLayoutSpec newWithStyle:{} children:@[foregroundNode]] + backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[foregroundNode]] background:backgroundNode]]; [self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil]; diff --git a/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm index 9ff4bf219e..994acee8c4 100644 --- a/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm @@ -61,7 +61,7 @@ static NSString *nameForInsets(UIEdgeInsets insets) ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode] + backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode] background:backgroundNode]; static ASSizeRange kVariableSize = {{0, 0}, {300, 300}}; @@ -82,7 +82,7 @@ static NSString *nameForInsets(UIEdgeInsets insets) ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode] + backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode] background:backgroundNode]; static ASSizeRange kFixedSize = {{300, 300}, {300, 300}}; @@ -104,7 +104,7 @@ static NSString *nameForInsets(UIEdgeInsets insets) ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild:[ASInsetLayoutSpec newWithInsets:insets child:foregroundNode] + backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode] background:backgroundNode]; static ASSizeRange kFixedSize = {{300, 300}, {300, 300}}; diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m index 0141d225e6..4cd9232f05 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m @@ -55,7 +55,7 @@ { ASLayout *layout = [layoutSpecUnderTest measureWithSizeRange:sizeRange]; layout.position = CGPointZero; - layout = [ASLayout newWithLayoutableObject:self size:layout.size sublayouts:@[layout]]; + layout = [ASLayout layoutWithLayoutableObject:self size:layout.size sublayouts:@[layout]]; _layoutUnderTest = [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { return [self.subnodes containsObject:evaluatedLayout.layoutableObject]; }]; diff --git a/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm index 066a6c47b5..f024de9faa 100644 --- a/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm @@ -34,10 +34,10 @@ static const ASSizeRange kSize = {{320, 320}, {320, 320}}; ASLayoutSpec *layoutSpec = [ASOverlayLayoutSpec - newWithChild:backgroundNode + overlayLayoutSpecWithChild:backgroundNode overlay: [ASCenterLayoutSpec - newWithCenteringOptions:ASCenterLayoutSpecCenteringXY + centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:{} child:foregroundNode]]; diff --git a/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm index ffe30e0f0d..5345a84649 100644 --- a/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm @@ -30,7 +30,7 @@ static const ASSizeRange kFixedSize = {{0, 0}, {100, 100}}; ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]); subnode.staticSize = childSize; - ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec newWithRatio:ratio child:subnode]; + ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:subnode]; [self testLayoutSpec:layoutSpec sizeRange:kFixedSize subnodes:@[subnode] identifier:identifier]; } diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 21a885444e..9b6c3d655d 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -11,6 +11,7 @@ #import "ASLayoutSpecSnapshotTestsHelper.h" #import "ASStackLayoutSpec.h" +#import "ASStackLayoutSpecUtilities.h" #import "ASBackgroundLayoutSpec.h" #import "ASRatioLayoutSpec.h" #import "ASInsetLayoutSpec.h" @@ -79,7 +80,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild:[ASStackLayoutSpec newWithStyle:style children:children] + backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction spacing:style.spacing justifyContent:style.justifyContent alignItems:style.alignItems children:children] background:backgroundNode]; NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode]; @@ -179,17 +180,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASLayoutSpec *layoutSpec = [ASInsetLayoutSpec - newWithInsets:{10, 10, 10 ,10} + insetLayoutSpecWithInsets:{10, 10, 10 ,10} child: [ASBackgroundLayoutSpec - newWithChild: + backgroundLayoutSpecWithChild: [ASStackLayoutSpec - newWithStyle:{ - .direction = ASStackLayoutDirectionVertical, - .spacing = 10, - .alignItems = ASStackLayoutAlignItemsStretch - } - children:@[]] + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[]] background:backgroundNode]]; // width 300px; height 0-300px @@ -257,7 +253,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode2.staticSize = {50, 50}; - ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec newWithRatio:1.5 child:subnode1]; + ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; child1.flexBasis = ASRelativeDimensionMakeWithPercent(1); child1.flexGrow = YES; child1.flexShrink = YES; @@ -481,7 +477,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {10, 0}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; - ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec newWithRatio:1.0 child:subnodes[2]]; + ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; child2.flexGrow = YES; child2.flexShrink = YES; @@ -489,16 +485,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec - newWithChild: + backgroundLayoutSpecWithChild: [ASInsetLayoutSpec - newWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) + insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) child: - [ASStackLayoutSpec - newWithStyle:{ - .direction = ASStackLayoutDirectionHorizontal, - .alignItems = ASStackLayoutAlignItemsStretch, - } - children:@[subnodes[1], child2,]]] + [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[subnodes[1], child2,]] + ] background:subnodes[0]]; static ASSizeRange kSize = {{300, 0}, {300, INFINITY}}; diff --git a/examples/Kittens/Sample/BlurbNode.m b/examples/Kittens/Sample/BlurbNode.m index a4802daf5c..693ec0cd03 100644 --- a/examples/Kittens/Sample/BlurbNode.m +++ b/examples/Kittens/Sample/BlurbNode.m @@ -81,9 +81,8 @@ static NSString *kLinkAttributeName = @"PlaceKittenNodeLinkAttributeName"; centerSpec.sizingOptions = ASCenterLayoutSpecSizingOptionMinimumY; centerSpec.child = _textNode; - - return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding) - child:centerSpec]; + UIEdgeInsets padding =UIEdgeInsetsMake(kTextPadding, kTextPadding, kTextPadding, kTextPadding); + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:padding child:centerSpec]; } #else - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize From b93faab7f5ebc985a3b99b837d625e5a47508d81 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 9 Aug 2015 23:18:25 +0300 Subject: [PATCH 075/145] Improve relayout when constrained size of all nodes is changed: - In -layoutSubviews of table and collection views, detect changes that cause a different constrained size for nodes, and trigger relayout immediately. - Orientation change can be handled by this solution. So, no need to observe to its events. - Update Kittens example to support iPad (easier to catch bugs on these devices) and add a title to navigation bar (looks a bit nicer). --- AsyncDisplayKit/ASCollectionView.mm | 38 ++++--------------- AsyncDisplayKit/ASTableView.mm | 33 +++------------- AsyncDisplayKit/Details/ASDataController.h | 4 +- AsyncDisplayKit/Details/ASDataController.mm | 17 ++++----- AsyncDisplayKit/Private/ASInternalHelpers.h | 4 -- AsyncDisplayKit/Private/ASInternalHelpers.mm | 15 -------- .../Kittens/Sample.xcodeproj/project.pbxproj | 2 + examples/Kittens/Sample/ViewController.m | 1 + 8 files changed, 27 insertions(+), 87 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index d6fa28ebc2..8223d5208e 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -125,7 +125,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASBatchContext *_batchContext; - BOOL _pendingRelayoutForAllRows; + CGSize _maxSizeForNodesConstrainedSize; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -170,16 +170,13 @@ static BOOL _isInterceptedSelector(SEL sel) _performingBatchUpdates = NO; _batchUpdateBlocks = [NSMutableArray array]; + + _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; + + _maxSizeForNodesConstrainedSize = self.bounds.size; [self registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; - if (ASSystemVersionLessThan8()) { - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; - } - - _collectionViewLayoutImplementsInsetSection = [layout respondsToSelector:@selector(sectionInset)]; - return self; } @@ -189,11 +186,6 @@ static BOOL _isInterceptedSelector(SEL sel) // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; - - if (ASSystemVersionLessThan8()) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - } } #pragma mark - @@ -480,26 +472,12 @@ static BOOL _isInterceptedSelector(SEL sel) } } - -#pragma mark - -#pragma mark Orientation Change Handling - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - _pendingRelayoutForAllRows = YES; -} - -- (void)deviceOrientationDidChange -{ - _pendingRelayoutForAllRows = YES; -} - - (void)layoutSubviews { [super layoutSubviews]; - if (_pendingRelayoutForAllRows) { - _pendingRelayoutForAllRows = NO; + if (! CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, self.bounds.size)) { + _maxSizeForNodesConstrainedSize = self.bounds.size; [self performBatchAnimated:NO updates:^{ [_dataController relayoutAllRows]; } completion:nil]; @@ -562,7 +540,7 @@ static BOOL _isInterceptedSelector(SEL sel) if (_asyncDataSourceImplementsConstrainedSizeForNode) { constrainedSize = [_asyncDataSource collectionView:self constrainedSizeForNodeAtIndexPath:indexPath]; } else { - CGSize maxSize = self.bounds.size; + CGSize maxSize = _maxSizeForNodesConstrainedSize; if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { maxSize.width = FLT_MAX; } else { diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index cf45cc5320..6a9a3bd339 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -146,9 +146,9 @@ static BOOL _isInterceptedSelector(SEL sel) NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; CGFloat _contentOffsetAdjustment; - BOOL _pendingRelayoutForAllRows; - BOOL _asyncDataSourceImplementsConstrainedSizeForNode; + + CGFloat _maxWidthForNodesConstrainedSize; } @property (atomic, assign) BOOL asyncDataSourceLocked; @@ -202,10 +202,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _automaticallyAdjustsContentOffset = NO; - if (ASSystemVersionLessThan8()) { - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil]; - } + _maxWidthForNodesConstrainedSize = self.bounds.size.width; } - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style @@ -243,11 +240,6 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { // This bug might be iOS 7-specific. super.delegate = nil; super.dataSource = nil; - - if (ASSystemVersionLessThan8()) { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications]; - } } #pragma mark - @@ -373,25 +365,12 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { [_dataController endUpdatesAnimated:animated completion:completion]; } -#pragma mark - -#pragma mark Orientation Change Handling - -- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection -{ - _pendingRelayoutForAllRows = YES; -} - -- (void)deviceOrientationDidChange -{ - _pendingRelayoutForAllRows = YES; -} - - (void)layoutSubviews { [super layoutSubviews]; - if (_pendingRelayoutForAllRows) { - _pendingRelayoutForAllRows = NO; + if (_maxWidthForNodesConstrainedSize != self.bounds.size.width) { + _maxWidthForNodesConstrainedSize = self.bounds.size.width; [self beginUpdates]; [_dataController relayoutAllRows]; [self endUpdates]; @@ -812,7 +791,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } // Default size range - return ASSizeRangeMake(CGSizeZero, CGSizeMake(self.bounds.size.width, FLT_MAX)); + return ASSizeRangeMake(CGSizeZero, CGSizeMake(_maxWidthForNodesConstrainedSize, FLT_MAX)); } - (void)dataControllerLockDataSource diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 7221d688cf..68e586daa8 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -156,8 +156,8 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** - * Re-measures all loaded nodes. Used for external relayout (relayout that is caused by a change in constrained size of each and every cell node, - * for example, after an orientation change). + * Re-measures all loaded nodes. Used to respond to a change in size of the containing view + * (e.g. ASTableView or ASCollectionView after an orientation change). */ - (void)relayoutAllRows; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4d3d42fcc7..4cfc03270d 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -564,15 +564,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_completedNodes); - NSArray *loadedNodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); - - for (NSUInteger i = 0; i < loadedNodes.count && i < indexPaths.count; i++) { - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[i]]; - ASCellNode *node = loadedNodes[i]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); - } + [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + ASSizeRange constrainedSize = [_dataSource dataController:self + constrainedSizeForNodeAtIndexPath:[NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; }]; } diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 8693440eff..e20c8c999a 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -24,8 +24,4 @@ CGFloat ASCeilPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f); -BOOL ASSystemVersionLessThan8(); - -BOOL ASSystemVersionLessThanVersion(NSString *version); - ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.mm b/AsyncDisplayKit/Private/ASInternalHelpers.mm index f230e1e49c..d161523f2b 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.mm +++ b/AsyncDisplayKit/Private/ASInternalHelpers.mm @@ -61,18 +61,3 @@ CGFloat ASRoundPixelValue(CGFloat f) { return roundf(f * ASScreenScale()) / ASScreenScale(); } - -BOOL ASSystemVersionLessThan8() -{ - static BOOL lessThan8; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - lessThan8 = ASSystemVersionLessThanVersion(@"8"); - }); - return lessThan8; -} - -BOOL ASSystemVersionLessThanVersion(NSString *version) -{ - return [[[UIDevice currentDevice] systemVersion] compare:version options:NSNumericSearch] == NSOrderedAscending; -} diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index fde58763d9..293b513d1e 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -314,6 +314,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -326,6 +327,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 7.1; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index 02760a6355..7989fe15ce 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -59,6 +59,7 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell _blurbNodeIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + self.title = @"Kittens"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(toggleEditingMode)]; From 2ea7c541a61cef7e86ab0ee2d8dbd93855423fe0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 13 Aug 2015 07:17:24 +0300 Subject: [PATCH 076/145] When a new preferredFrameSize is set, previously calculated layout should be invalidated so that the size will be picked up in the next layout pass. --- AsyncDisplayKit/ASDisplayNode.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index e14f256b5a..32b4eee832 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1387,7 +1387,10 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)setPreferredFrameSize:(CGSize)preferredFrameSize { ASDN::MutexLocker l(_propertyLock); - _preferredFrameSize = preferredFrameSize; + if (! CGSizeEqualToSize(_preferredFrameSize, preferredFrameSize)) { + _preferredFrameSize = preferredFrameSize; + [self invalidateCalculatedLayout]; + } } - (CGSize)preferredFrameSize From f7f20ca8b74170f1a896f541dfe505d33b79679b Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Thu, 13 Aug 2015 07:18:32 +0300 Subject: [PATCH 077/145] preferredFrameSize should be used even if neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses. --- AsyncDisplayKit/ASDisplayNode.mm | 14 ++++++++------ AsyncDisplayKit/Private/ASDisplayNodeInternal.h | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 32b4eee832..07a546ea85 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -150,8 +150,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(touchesEnded:withEvent:))) { overrides |= ASDisplayNodeMethodOverrideTouchesEnded; } - if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(calculateSizeThatFits:))) { - overrides |= ASDisplayNodeMethodOverrideCalculateSizeThatFits; + if (ASDisplayNodeSubclassOverridesSelector([self class], @selector(layoutSpecThatFits:))) { + overrides |= ASDisplayNodeMethodOverrideLayoutSpecThatFits; } _methodOverrides = overrides; @@ -1337,10 +1337,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { ASDisplayNodeAssertThreadAffinity(self); - if (_methodOverrides & ASDisplayNodeMethodOverrideCalculateSizeThatFits) { - CGSize size = [self calculateSizeThatFits:constrainedSize.max]; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; - } else { + if (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) { id layoutSpec = [self layoutSpecThatFits:constrainedSize]; ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize]; // Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct. @@ -1351,6 +1348,11 @@ static NSInteger incrementIfFound(NSInteger i) { return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) { return [_subnodes containsObject:evaluatedLayout.layoutableObject]; }]; + } else { + // If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used, + // assume that the default implementation of -calculateSizeThatFits: returns it. + CGSize size = [self calculateSizeThatFits:constrainedSize.max]; + return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)]; } } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index c3e476a441..5a8883ed02 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -28,7 +28,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1, ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2, ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3, - ASDisplayNodeMethodOverrideCalculateSizeThatFits = 1 << 4 + ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4 }; @class _ASPendingState; From df730d6771d07b663ea4a2ef63f0a5a8088fb2d3 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 13 Aug 2015 13:53:08 -0400 Subject: [PATCH 078/145] Return CGSizeZero if ASTextNode's text length or attributed string is nil --- AsyncDisplayKit/Details/ASTextNodeRenderer.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm index b17ae2833f..af28bf1719 100644 --- a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm +++ b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm @@ -173,9 +173,13 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3; { ASDN::MutexLocker l(_textKitLock); + if (_attributedString.string.length == 0) { + _calculatedSize = CGSizeZero; + return; + } + [self _initializeTextKitComponentsIfNeeded]; - // Force glyph generation and layout, which may not have happened yet (and // isn't triggered by -usedRectForTextContainer:). [_layoutManager ensureLayoutForTextContainer:_textContainer]; From 8028364be48ae8449fea3387883e70ac424154e3 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 13 Aug 2015 13:57:54 -0400 Subject: [PATCH 079/145] Add test coverage for ASTextNodeRenderer to validate nil and empty strings produce CGSizeZero --- AsyncDisplayKitTests/ASTextNodeRendererTests.m | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/AsyncDisplayKitTests/ASTextNodeRendererTests.m b/AsyncDisplayKitTests/ASTextNodeRendererTests.m index a9bf427af4..bcfb7c12ce 100644 --- a/AsyncDisplayKitTests/ASTextNodeRendererTests.m +++ b/AsyncDisplayKitTests/ASTextNodeRendererTests.m @@ -69,6 +69,22 @@ XCTAssertTrue(size.height > 0, @"Should have a nonzero height"); } +- (void)testCalculateSizeWithEmptyString +{ + _attributedString = [[NSAttributedString alloc] initWithString:@""]; + [self setUpRenderer]; + CGSize size = [_renderer size]; + XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, size), @"Empty NSAttributedString should result in CGSizeZero"); +} + +- (void)testCalculateSizeWithNilString +{ + _attributedString = nil; + [self setUpRenderer]; + CGSize size = [_renderer size]; + XCTAssertTrue(CGSizeEqualToSize(CGSizeZero, size), @"Nil NSAttributedString should result in CGSizeZero"); +} + - (void)testNumberOfLines { [self setUpRenderer]; From 15975a67e8fa1507029717048cd5012b0b814f26 Mon Sep 17 00:00:00 2001 From: Sal Date: Sun, 16 Aug 2015 18:24:20 -0400 Subject: [PATCH 080/145] use attributedString.length instead of attributedString.string.length to check for empty strings --- AsyncDisplayKit/Details/ASTextNodeRenderer.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm index af28bf1719..84b290e08e 100644 --- a/AsyncDisplayKit/Details/ASTextNodeRenderer.mm +++ b/AsyncDisplayKit/Details/ASTextNodeRenderer.mm @@ -173,7 +173,7 @@ static const CGFloat ASTextNodeRendererTextCapHeightPadding = 1.3; { ASDN::MutexLocker l(_textKitLock); - if (_attributedString.string.length == 0) { + if (_attributedString.length == 0) { _calculatedSize = CGSizeZero; return; } From f7e94f42ed3d26a4b418ce549e64eae9da5de660 Mon Sep 17 00:00:00 2001 From: Ethan Nagel Date: Mon, 17 Aug 2015 12:23:25 -0700 Subject: [PATCH 081/145] Node init performance: Use share instances of UIColor and NSAttributedString for defaults. --- AsyncDisplayKit/ASDisplayNodeExtras.h | 1 + AsyncDisplayKit/ASDisplayNodeExtras.mm | 19 ++++++++++++++++++- AsyncDisplayKit/ASTextNode.mm | 12 +++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.h b/AsyncDisplayKit/ASDisplayNodeExtras.h index bb042be975..5ce6ac2ba5 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.h +++ b/AsyncDisplayKit/ASDisplayNodeExtras.h @@ -60,6 +60,7 @@ extern id ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDi extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c); extern UIColor *ASDisplayNodeDefaultPlaceholderColor(); +extern UIColor *ASDisplayNodeDefaultTintColor(); /** Disable willAppear / didAppear / didDisappear notifications for a sub-hierarchy, then re-enable when done. Nested calls are supported. diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.mm b/AsyncDisplayKit/ASDisplayNodeExtras.mm index b8a4c1a4eb..8d2e261570 100644 --- a/AsyncDisplayKit/ASDisplayNodeExtras.mm +++ b/AsyncDisplayKit/ASDisplayNodeExtras.mm @@ -126,7 +126,24 @@ extern id ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) UIColor *ASDisplayNodeDefaultPlaceholderColor() { - return [UIColor colorWithWhite:0.95 alpha:1.0]; + static UIColor *defaultPlaceholderColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultPlaceholderColor = [UIColor colorWithWhite:0.95 alpha:1.0]; + }); + return defaultPlaceholderColor; +} + +UIColor *ASDisplayNodeDefaultTintColor() +{ + static UIColor *defaultTintColor; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTintColor = [UIColor colorWithRed:0.0 green:0.478 blue:1.0 alpha:1.0]; + }); + return defaultTintColor; } #pragma mark - Hierarchy Notifications diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 8b3158a905..7ca68f5177 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -130,7 +130,7 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.needsDisplayOnBoundsChange = YES; _truncationMode = NSLineBreakByWordWrapping; - _truncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + _truncationAttributedString = DefaultTruncationAttributedString(); // The common case is for a text node to be non-opaque and blended over some background. self.opaque = NO; @@ -972,6 +972,16 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) #pragma mark - Truncation Message +static NSAttributedString *DefaultTruncationAttributedString() +{ + static NSAttributedString *defaultTruncationAttributedString; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultTruncationAttributedString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"\u2026", @"Default truncation string")]; + }); + return defaultTruncationAttributedString; +} + - (void)setTruncationAttributedString:(NSAttributedString *)truncationAttributedString { // No-op if they're exactly equal (avoid redrawing) From e005a83a2bec1dd7315a570b5ea3651edb87ccf0 Mon Sep 17 00:00:00 2001 From: Scott Goodson Date: Mon, 17 Aug 2015 17:19:51 -0700 Subject: [PATCH 082/145] Revert "Switch to use only layers for offscreen working range window." There is an unsolved issue with this optimization that may cause crashes in certain cases. Reverting until it's properly resolved. It doesn't appear to be possible to cause the issue in the test apps used to develop it. This reverts commit 3f982b5c9a7e4a557c102268f11f71b58085ffef. --- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 1d47b7d10c..b9cf41c87a 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -39,12 +39,8 @@ [node recursivelySetDisplaySuspended:NO]; - // 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]; + // add the node to an off-screen window to force display and preserve its contents + [[self.class workingWindow] addSubnode:node]; } - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType @@ -53,7 +49,7 @@ ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); [node recursivelySetDisplaySuspended:YES]; - [node.layer removeFromSuperlayer]; + [node.view removeFromSuperview]; [node recursivelyClearContents]; } From 0ea276e3abdeb6687c502b9ad2fd04f3ae854d5d Mon Sep 17 00:00:00 2001 From: rcancro Date: Sat, 22 Aug 2015 18:47:16 -0700 Subject: [PATCH 083/145] huy's comments --- AsyncDisplayKit/ASDisplayNode.mm | 5 -- .../Layout/ASBaselineStackLayoutSpec.h | 1 - .../Layout/ASBaselineStackLayoutSpec.mm | 9 +- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 - .../Private/ASBaselineStackPositionedLayout.h | 2 +- .../ASBaselineStackPositionedLayout.mm | 86 ++++++++++++++----- .../Private/ASStackPositionedLayout.mm | 3 - 7 files changed, 70 insertions(+), 40 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 07a546ea85..c02c053e25 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -453,11 +453,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) #pragma mark - -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return 0; -} - - (CGSize)measure:(CGSize)constrainedSize { return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h index df613f42fc..789e82f235 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h @@ -30,7 +30,6 @@ typedef struct { ASBaselineStackLayoutBaselineAlignment baselineAlignment; } ASBaselineStackLayoutSpecStyle; - /** A specialized version of a stack layout that aligns its children on a baseline. This spec only works with ASBaselineStackLayoutable children. diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm index 52d6be3870..bfe8a512b7 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm @@ -27,7 +27,7 @@ @implementation ASBaselineStackLayoutSpec { - ASBaselineStackLayoutSpecStyle _textStyle; + ASBaselineStackLayoutSpecStyle _style; std::vector> _stackChildren; ASDN::RecursiveMutex _propertyLock; } @@ -41,7 +41,7 @@ ASBaselineStackLayoutSpec *spec = [super new]; if (spec) { - spec->_textStyle = style; + spec->_style = style; spec->_stackChildren = std::vector>(); for (id child in children) { ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineStackLayoutable)], @"child must conform to ASStackLayoutable"); @@ -59,11 +59,11 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + ASStackLayoutSpecStyle stackStyle = _style.stackLayoutStyle; const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _style, constrainedSize); const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); @@ -73,7 +73,6 @@ _ascender = baselinePositionedLayout.ascender; _descender = baselinePositionedLayout.descender; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 242d6e3122..6a3e999db5 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -20,10 +20,6 @@ typedef struct { ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ 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*/ - BOOL baselineRelativeArrangement; } ASStackLayoutSpecStyle; /** diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h index 877da2062f..8f18216a98 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h @@ -21,6 +21,6 @@ struct ASBaselineStackPositionedLayout { /** Given a positioned layout, computes each child position using baseline alignment. */ static ASBaselineStackPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &textStyle, + const ASBaselineStackLayoutSpecStyle &style, const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm index 42ab80d339..042f8479df 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm @@ -16,14 +16,14 @@ static CGFloat baselineForItem(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *layout) { - __weak id textChild = (id) layout.layoutableObject; + __weak id child = (id) layout.layoutableObject; switch (style.baselineAlignment) { case ASBaselineStackLayoutBaselineAlignmentNone: return 0; case ASBaselineStackLayoutBaselineAlignmentFirst: - return textChild.ascender; + return child.ascender; case ASBaselineStackLayoutBaselineAlignmentLast: - return layout.size.height + textChild.descender; + return layout.size.height + child.descender; } } @@ -34,10 +34,10 @@ static CGFloat baselineOffset(const ASBaselineStackLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id textChild = (id)l.layoutableObject; + __weak id child = (id)l.layoutableObject; switch (style.baselineAlignment) { case ASBaselineStackLayoutBaselineAlignmentFirst: - return maxAscender - textChild.ascender; + return maxAscender - child.ascender; case ASBaselineStackLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineStackLayoutBaselineAlignmentNone: @@ -56,29 +56,58 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, } ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &textStyle, + const ASBaselineStackLayoutSpecStyle &style, const ASSizeRange &constrainedSize) { - ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; + ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle; - - // Get the largest distance from the top of the stack to a baseline. This is the baseline we will align to. + /* 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: + + Hello! Why, hello there! How + are you today? + + The first node has a font of size 14, the second a font of size 12. The first node will have a baseline offset of + the ascender of a font of size 14, the second will have a baseline of the ascender of a font of size 12. The first + baseline will be larger so we will keep that as the max baseline. + + However, if were to align from the last baseline we'd find the max baseline by taking the height of node and adding + the font's descender (its negative). In the case of the first node, which is only 1 line, this should be the same value as the ascender. + The second node, however, has a larger height and there will have a larger baseline offset. + */ const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return baselineForItem(textStyle, a) < baselineForItem(textStyle, b); + return baselineForItem(style, a) < baselineForItem(style, b); }); - const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(style, *baselineIt); - // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASBaselineStackLayoutSpec as its ascender. + /* + Step 2: Find the max ascender for all of the children. + Imagine 3 nodes aligned horizontally, all with the same text but with font sizes of 12, 14, 16. Because it is has the largest + ascender node with font size of 16 will not need to move, the other two nodes will align to this node's baseline. The offset we will use + for each node is our computed maxAscender - node.ascender. If the 16pt node had an ascender of 10 and the 14pt node + had an ascender of 8, that means we will offset the 14pt node by 2 pts. + + Note: if we are alinging to the last baseline, then we don't need this value in our computation. However, we do want + 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; }); const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + /* + Step 3: Take each child and update its layout position based on the baseline offset. + + If this is a horizontal stack, we take a positioned child and add to its y offset to align it to the maxBaseline of the children. + If this is a vertical stack, we add the child's descender to the location of the next child to position. This will ensure the + spacing between the two nodes is from the baseline, not the bounding box. + + */ CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; - p = p + directionPoint(stackStyle.direction, textChild.spacingBefore, 0); + __weak id child = (id) l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -88,17 +117,29 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const A } first = NO; - // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y - l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); + // Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position. + l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline)); - // If we are a vertical stack, add the item's descender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes - CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); + // 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) { + spacingAfterBaseline = child.descender; + } + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); return l; }); - // The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below). + /* + Step 4: Since we have been mucking with positions, there is a chance that our cross size has changed. Imagine a node with a font size of 40 + and another node with a font size of 12 but with multiple lines. We align these nodes to the first baseline, which will be the baseline of the node with + font size of 40 (max ascender). Now, we have to move the node with multiple lines down to the other node's baseline. This node with multiple lines will + extend below the first node farther than it did before aligning the baselines thus increasing the cross size. + + After finding the new cross size, we need to clamp it so that it fits within the constrainted size. + + */ const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](ASLayout *a, ASLayout *b) { return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); @@ -108,7 +149,10 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const A const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // find the child with the largest height. Use that child's descender as the descender to pass back to the ASBaselineStackLayoutSpec. + /* + 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. + */ 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; }); diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index d106d0618b..84bbdac502 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -27,12 +27,9 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - default: - return 0; } } - static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, const CGFloat offset, const ASStackUnpositionedLayout &unpositionedLayout, From f289b3345ab4dabcef9847a46e43301a1794c2f8 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 12 Aug 2015 16:55:12 -0700 Subject: [PATCH 084/145] Add baseline support to ASStackLayoutSpec --- AsyncDisplayKit/ASDisplayNode.mm | 4 +++ AsyncDisplayKit/ASTextNode.mm | 3 +++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 ++ AsyncDisplayKit/Layout/ASLayoutable.h | 10 +++++++ AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 4 +++ AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 +++ .../Private/ASStackLayoutSpecUtilities.h | 4 +++ .../Private/ASStackPositionedLayout.mm | 27 +++++++++++++++++-- 8 files changed, 56 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index c02c053e25..9c9a9a2625 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -48,6 +48,8 @@ @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; +@synthesize ascender = _ascender; +@synthesize descender = _descender; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -157,6 +159,8 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; + _ascender = 0; + _descender = 0; } - (id)init diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 68f4f1fdab..1dcd003260 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -355,6 +355,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); + + self.ascender = [[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender]; + self.descender = [[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender]; } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 8b3efd6f4d..377fbe144b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -24,6 +24,8 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize ascender = _ascender; +@synthesize descender = _descender; + (instancetype)new { diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 30bf7af5e8..e6f7df7bdc 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -57,6 +57,16 @@ */ @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; + /** * @abstract Calculate a layout based on given size range. * diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 6663ad2ae3..0e6a600ec3 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -23,4 +23,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 29f20603c5..77267eb70d 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -47,6 +47,10 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { ASStackLayoutAlignItemsCenter, /** Expand children to fill cross axis */ 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, }; typedef struct { diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 4f5eff0aeb..8a6f7f5126 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -55,6 +55,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 420258460f..5d32c7b4eb 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -15,9 +15,21 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" +static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, + const ASStackUnpositionedItem &item) { + const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); + if (alignItems == ASStackLayoutAlignItemsBaselineFirst) { + return item.child.ascender; + } else if (alignItems == ASStackLayoutAlignItemsBaselineLast) { + return item.layout.size.height + item.child.descender; + } + return 0; +} + static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, - const CGFloat crossSize) + const CGFloat crossSize, + const CGFloat maxBaseline) { switch (alignment(l.child.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: @@ -27,9 +39,14 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; + case ASStackLayoutAlignItemsBaselineFirst: + return maxBaseline - l.child.ascender; + case ASStackLayoutAlignItemsBaselineLast: + return maxBaseline - baselineForItem(style, l); } } + static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, const CGFloat offset, const ASStackUnpositionedLayout &unpositionedLayout, @@ -44,6 +61,12 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &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); + + // Find the maximum height for the baseline + const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ + return baselineForItem(style, a) < baselineForItem(style, b); + }); + const CGFloat maxBaseLine = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; @@ -53,7 +76,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); return l.layout; }); From cf428831a4837ae607429ee71c0fa41800267020 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Sat, 15 Aug 2015 08:17:43 -0700 Subject: [PATCH 085/145] Added baseline spacing for vertical stack views. --- AsyncDisplayKit/ASTextNode.mm | 4 ++-- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 ++++ AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 7 +++++++ AsyncDisplayKit/Private/ASStackPositionedLayout.h | 2 ++ AsyncDisplayKit/Private/ASStackPositionedLayout.mm | 6 ++++-- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 1dcd003260..1278a66999 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -356,8 +356,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - self.ascender = [[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender]; - self.descender = [[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender]; + self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; + self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 77267eb70d..77ce335891 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -62,6 +62,10 @@ typedef struct { ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ 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*/ + BOOL baselineRelativeArrangement; } ASStackLayoutSpecStyle; /** diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 75b699c55b..20ed12590f 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -20,11 +20,13 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" +#import "ASThread.h" @implementation ASStackLayoutSpec { ASStackLayoutSpecStyle _style; std::vector> _children; + ASDN::RecursiveMutex _propertyLock; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -51,6 +53,11 @@ 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()]; + + ASDN::MutexLocker l(_propertyLock); + self.ascender = positionedLayout.ascender; + self.descender = positionedLayout.desender; + return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index 211bda5b11..a4d2718ff3 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,6 +17,8 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; + const CGFloat ascender; + const CGFloat desender; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 5d32c7b4eb..ccdeb0407f 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -77,10 +77,12 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); + + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0;; + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize}; + return {stackedChildren, crossSize, maxBaseLine, maxBaseLine == 0 ? 0 : crossSize - maxBaseLine}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From 3fd241e87e5c972315f7ef7b8415003476ba230a Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Sat, 15 Aug 2015 09:45:14 -0700 Subject: [PATCH 086/145] fixed vertical spacing bug and typo --- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 2 +- AsyncDisplayKit/Private/ASStackPositionedLayout.h | 2 +- AsyncDisplayKit/Private/ASStackPositionedLayout.mm | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 20ed12590f..5e36099d17 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -56,7 +56,7 @@ ASDN::MutexLocker l(_propertyLock); self.ascender = positionedLayout.ascender; - self.descender = positionedLayout.desender; + self.descender = positionedLayout.descender; return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index a4d2718ff3..b8664a3855 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -18,7 +18,7 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; const CGFloat ascender; - const CGFloat desender; + const CGFloat descender; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index ccdeb0407f..4a78594cee 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -66,7 +66,7 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ return baselineForItem(style, a) < baselineForItem(style, b); }); - const CGFloat maxBaseLine = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); + const CGFloat maxBaseline = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; @@ -76,13 +76,13 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseLine)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0;; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseLine, maxBaseLine == 0 ? 0 : crossSize - maxBaseLine}; + return {stackedChildren, crossSize, maxBaseline, maxBaseline == 0 ? 0 : maxBaseline - crossSize}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From ef89f758bc1bb90b30c4fb0cc275bf24eb02712e Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Wed, 19 Aug 2015 16:59:23 -0700 Subject: [PATCH 087/145] flailing --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 ++ AsyncDisplayKit/ASDisplayNode.h | 4 +- AsyncDisplayKit/ASDisplayNode.mm | 9 +-- AsyncDisplayKit/ASTextNode.mm | 24 +++++++- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 7 ++- AsyncDisplayKit/Layout/ASLayoutable.h | 47 --------------- AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 49 ++++++++++++++-- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 44 +------------- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 17 ++++-- AsyncDisplayKit/Layout/ASStackLayoutable.h | 57 +++++++++++++++++++ .../Private/ASStackLayoutSpecUtilities.h | 4 -- .../Private/ASStackPositionedLayout.h | 3 +- .../Private/ASStackPositionedLayout.mm | 15 ++--- .../Private/ASStackUnpositionedLayout.h | 4 +- .../Private/ASStackUnpositionedLayout.mm | 12 ++-- 16 files changed, 172 insertions(+), 134 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStackLayoutable.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index aee3b1867a..7cff23c102 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,8 @@ 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, ); }; }; + 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, ); }; }; 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, ); }; }; @@ -556,6 +558,7 @@ 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 = ""; }; + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -958,6 +961,7 @@ ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */, ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */, ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */, + 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */, AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, @@ -1004,6 +1008,7 @@ files = ( AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */, AC47D9451B3BB41900AAEE9D /* ASRelativeSize.h in Headers */, + 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */, ACF6ED511B17847A00DA7C62 /* ASStackUnpositionedLayout.h in Headers */, ACF6ED2D1B17843500DA7C62 /* ASRatioLayoutSpec.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, @@ -1170,6 +1175,7 @@ B350620C1B010EFD0018CF92 /* ASTableViewProtocols.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, + 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */, 34EFC7731B701D0700AD841F /* ASStaticLayoutSpec.h in Headers */, B35062081B010EFD0018CF92 /* ASScrollNode.h in Headers */, B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 7fcfbd5999..974bbe1272 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 9c9a9a2625..07a546ea85 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -48,8 +48,6 @@ @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; -@synthesize ascender = _ascender; -@synthesize descender = _descender; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) { @@ -159,8 +157,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; - _ascender = 0; - _descender = 0; } - (id)init @@ -457,6 +453,11 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) #pragma mark - +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return 0; +} + - (CGSize)measure:(CGSize)constrainedSize { return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 1278a66999..8107fc6ece 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,6 +17,7 @@ #import #import +#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -105,6 +106,9 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) ASTextNodeShadower *_shadower; UILongPressGestureRecognizer *_longPressGestureRecognizer; + + CGFloat _topBaseline; + CGFloat _bottomBaseline; } #pragma mark - NSObject @@ -356,8 +360,24 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - self.ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; - self.descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * [UIScreen mainScreen].scale)/[UIScreen mainScreen].scale; + _topBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + _bottomBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); +} + +#pragma mark - Baseline computation + +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + switch (baselineAlignmentType) { + case ASStackLayoutAlignItemsLastBaseline: + return self.calculatedSize.height + _bottomBaseline; + + case ASStackLayoutAlignItemsFirstBaseline: + return _topBaseline; + + default: + return 0; + } } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index c2602824e7..e92bbf75cc 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,9 +8,9 @@ * */ -#import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 377fbe144b..34f6206e1b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -24,8 +24,6 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; -@synthesize ascender = _ascender; -@synthesize descender = _descender; + (instancetype)new { @@ -36,6 +34,11 @@ return spec; } +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return 0; +} + #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index e6f7df7bdc..4ac01ee05f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -20,53 +20,6 @@ */ @protocol ASLayoutable -/** - * @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; - /** * @abstract Calculate a layout based on given size range. * diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 0e6a600ec3..39e51b65c8 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -8,6 +8,49 @@ * */ +/** The direction children are stacked in */ +typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { + /** Children are stacked vertically */ + ASStackLayoutDirectionVertical, + /** Children are stacked horizontally */ + ASStackLayoutDirectionHorizontal, +}; + +/** If no children are flexible, how should this spec justify its children in the available space? */ +typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { + /** + On overflow, children overflow out of this spec's bounds on the right/bottom side. + On underflow, children are left/top-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentStart, + /** + On overflow, children are centered and overflow on both sides. + On underflow, children are centered within this spec's bounds in the stacking direction. + */ + ASStackLayoutJustifyContentCenter, + /** + On overflow, children overflow out of this spec's bounds on the left/top side. + On underflow, children are right/bottom-aligned within this spec's bounds. + */ + ASStackLayoutJustifyContentEnd, +}; + +/** Orientation of children along cross axis */ +typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { + /** Align children to start of cross axis */ + ASStackLayoutAlignItemsStart, + /** Align children with end of cross axis */ + ASStackLayoutAlignItemsEnd, + /** Center children on cross axis */ + ASStackLayoutAlignItemsCenter, + /** Expand children to fill cross axis */ + ASStackLayoutAlignItemsStretch, + /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsFirstBaseline, + /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ + ASStackLayoutAlignItemsLastBaseline, +}; + /** Each child may override their parent stack's cross axis alignment. @see ASStackLayoutAlignItems @@ -23,8 +66,6 @@ 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, + + /** Note: All children in a stack must have the same baseline align type */ }; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 77ce335891..242d6e3122 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -9,49 +9,7 @@ */ #import - -/** The direction children are stacked in */ -typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) { - /** Children are stacked vertically */ - ASStackLayoutDirectionVertical, - /** Children are stacked horizontally */ - ASStackLayoutDirectionHorizontal, -}; - -/** If no children are flexible, how should this spec justify its children in the available space? */ -typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) { - /** - On overflow, children overflow out of this spec's bounds on the right/bottom side. - On underflow, children are left/top-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentStart, - /** - On overflow, children are centered and overflow on both sides. - On underflow, children are centered within this spec's bounds in the stacking direction. - */ - ASStackLayoutJustifyContentCenter, - /** - On overflow, children overflow out of this spec's bounds on the left/top side. - On underflow, children are right/bottom-aligned within this spec's bounds. - */ - ASStackLayoutJustifyContentEnd, -}; - -/** Orientation of children along cross axis */ -typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { - /** Align children to start of cross axis */ - ASStackLayoutAlignItemsStart, - /** Align children with end of cross axis */ - ASStackLayoutAlignItemsEnd, - /** Center children on cross axis */ - ASStackLayoutAlignItemsCenter, - /** Expand children to fill cross axis */ - 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, -}; +#import typedef struct { /** Specifies the direction children are stacked in. */ diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 5e36099d17..90d2cc7fd8 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -25,8 +25,9 @@ @implementation ASStackLayoutSpec { ASStackLayoutSpecStyle _style; - std::vector> _children; + std::vector> _children; ASDN::RecursiveMutex _propertyLock; + CGFloat _distanceToBaseline; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -34,8 +35,10 @@ ASStackLayoutSpec *spec = [super new]; if (spec) { spec->_style = style; - spec->_children = std::vector>(); - for (id child in children) { + spec->_children = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"child must conform to ASStackLayoutable"); + spec->_children.push_back(child); } } @@ -55,12 +58,16 @@ NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; ASDN::MutexLocker l(_propertyLock); - self.ascender = positionedLayout.ascender; - self.descender = positionedLayout.descender; + _distanceToBaseline = positionedLayout.distanceToBaseline; return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType +{ + return _distanceToBaseline; +} + @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h new file mode 100644 index 0000000000..643bed0382 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -0,0 +1,57 @@ +/* + * 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 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 in stack spec. The distance from the top of the object to its baseline. + */ +- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType; + +@end diff --git a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h index 8a6f7f5126..4f5eff0aeb 100644 --- a/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h +++ b/AsyncDisplayKit/Private/ASStackLayoutSpecUtilities.h @@ -55,10 +55,6 @@ 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.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index b8664a3855..ea9f56fd26 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,8 +17,7 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; - const CGFloat ascender; - const CGFloat descender; + const CGFloat distanceToBaseline; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 4a78594cee..1e667abd73 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -18,10 +18,8 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &item) { const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsBaselineFirst) { - return item.child.ascender; - } else if (alignItems == ASStackLayoutAlignItemsBaselineLast) { - return item.layout.size.height + item.child.descender; + if (alignItems == ASStackLayoutAlignItemsFirstBaseline || alignItems == ASStackLayoutAlignItemsLastBaseline) { + return [item.child distanceToBaseline:alignItems]; } return 0; } @@ -39,9 +37,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - case ASStackLayoutAlignItemsBaselineFirst: - return maxBaseline - l.child.ascender; - case ASStackLayoutAlignItemsBaselineLast: + case ASStackLayoutAlignItemsLastBaseline: + case ASStackLayoutAlignItemsFirstBaseline: return maxBaseline - baselineForItem(style, l); } } @@ -78,11 +75,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.descender : 0; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.layout.size.height - [l.child distanceToBaseline:style.alignItems] : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseline, maxBaseline == 0 ? 0 : maxBaseline - crossSize}; + return {stackedChildren, crossSize, maxBaseline}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 45f648192e..44b7770ccf 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -15,7 +15,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -30,7 +30,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 9eb83a5b0d..0c094bedfd 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,7 +18,7 @@ /** 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, @@ -186,7 +186,7 @@ static std::function isFlexibleInViolatio } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { return child.flexGrow && child.flexShrink; } @@ -195,7 +195,7 @@ ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) 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 +283,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,7 +292,7 @@ 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 { + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); @@ -312,7 +312,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) { From 9036ab8e7d0c6c8f563c0819e7523c4eb4a05d55 Mon Sep 17 00:00:00 2001 From: rcancro Date: Wed, 19 Aug 2015 22:03:02 -0700 Subject: [PATCH 088/145] new stack layout spec for text --- AsyncDisplayKit.xcodeproj/project.pbxproj | 14 +- AsyncDisplayKit/ASTextNode.h | 2 +- AsyncDisplayKit/ASTextNode.mm | 26 +--- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 - AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 10 -- AsyncDisplayKit/Layout/ASStackLayoutable.h | 10 +- .../Layout/ASStackTextLayoutSpec.h | 36 +++++ .../Layout/ASStackTextLayoutSpec.mm | 127 ++++++++++++++++++ .../Private/ASStackPositionedLayout.h | 1 - .../Private/ASStackPositionedLayout.mm | 10 +- 10 files changed, 194 insertions(+), 47 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h create mode 100644 AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 7cff23c102..29c10b3ca5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,10 @@ 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, ); }; }; + 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; + 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; @@ -447,7 +451,7 @@ 058D09D5195D050800B7D73C /* ASControlNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASControlNode.h; sourceTree = ""; }; 058D09D6195D050800B7D73C /* ASControlNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNode.m; sourceTree = ""; }; 058D09D7195D050800B7D73C /* ASControlNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+Subclasses.h"; sourceTree = ""; }; - 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09D8195D050800B7D73C /* ASDisplayNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASDisplayNode.h; sourceTree = ""; }; 058D09D9195D050800B7D73C /* ASDisplayNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASDisplayNode.mm; sourceTree = ""; }; 058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "ASDisplayNode+Subclasses.h"; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeExtras.h; sourceTree = ""; }; @@ -558,6 +562,8 @@ 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 = ""; }; + 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; + 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -967,6 +973,8 @@ ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1074,6 +1082,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, + 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1148,6 +1157,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, + 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1451,6 +1461,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, + 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1512,6 +1523,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index fc520c1232..681c2cb71e 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -30,7 +30,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 8107fc6ece..7ca68f5177 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -106,11 +106,11 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) ASTextNodeShadower *_shadower; UILongPressGestureRecognizer *_longPressGestureRecognizer; - - CGFloat _topBaseline; - CGFloat _bottomBaseline; } +@synthesize ascender = _ascender; +@synthesize descender = _descender; + #pragma mark - NSObject - (instancetype)init @@ -360,24 +360,8 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) } }); - _topBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - _bottomBaseline = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); -} - -#pragma mark - Baseline computation - -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - switch (baselineAlignmentType) { - case ASStackLayoutAlignItemsLastBaseline: - return self.calculatedSize.height + _bottomBaseline; - - case ASStackLayoutAlignItemsFirstBaseline: - return _topBaseline; - - default: - return 0; - } + _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/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 34f6206e1b..8b3efd6f4d 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -34,11 +34,6 @@ return spec; } -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return 0; -} - #pragma mark - Layout - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 90d2cc7fd8..c5c3660fc4 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -26,8 +26,6 @@ { ASStackLayoutSpecStyle _style; std::vector> _children; - ASDN::RecursiveMutex _propertyLock; - CGFloat _distanceToBaseline; } + (instancetype)newWithStyle:(ASStackLayoutSpecStyle)style children:(NSArray *)children @@ -57,17 +55,9 @@ const CGSize finalSize = directionSize(_style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; - ASDN::MutexLocker l(_propertyLock); - _distanceToBaseline = positionedLayout.distanceToBaseline; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return _distanceToBaseline; -} - @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 643bed0382..057ca8b0fd 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -49,9 +49,11 @@ */ @property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; -/** - * @abstract Used for baseline alignment in stack spec. The distance from the top of the object to its baseline. - */ -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType; +@end + +@protocol ASStackTextLayoutable + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; @end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h new file mode 100644 index 0000000000..ab5b80db19 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h @@ -0,0 +1,36 @@ +// +// ASStackTextLayoutSpec.h +// AsyncDisplayKit +// +// Created by ricky cancro on 8/19/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +/** Orientation of children along cross axis */ +typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { + ASStackTextLayoutBaselineAlignmentNone, + /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ + ASStackTextLayoutBaselineAlignmentFirst, + /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ + ASStackTextLayoutBaselineAlignmentLast, +}; + + +typedef struct { + /** Specifies the direction children are stacked in. */ + ASStackLayoutSpecStyle stackLayoutStyle; + + ASStackTextLayoutBaselineAlignment baselineAlignment; +} ASStackTextLayoutSpecStyle; + +@interface ASStackTextLayoutSpec : ASLayoutSpec + +/** + @param style Specifies how children are laid out. + @param children ASLayoutable children to be positioned. + */ ++ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; + +@end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm new file mode 100644 index 0000000000..a15f4c6686 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm @@ -0,0 +1,127 @@ +// +// ASStackTextLayoutSpec.m +// AsyncDisplayKit +// +// Created by ricky cancro on 8/19/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASStackTextLayoutSpec.h" +#import "ASStackLayoutable.h" + +#import +#import + +#import "ASBaseDefines.h" +#import "ASInternalHelpers.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" +#import "ASStackPositionedLayout.h" +#import "ASStackUnpositionedLayout.h" +#import "ASThread.h" + +static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, + const ASLayout *layout) { + + __weak id textChild = (id) layout.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + case ASStackTextLayoutBaselineAlignmentFirst: + return textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return textChild.descender; + } + +} + +static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, + const ASLayout *l, + const CGFloat maxBaseline) +{ + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentFirst: + case ASStackTextLayoutBaselineAlignmentLast: + return maxBaseline - baselineForItem(style, l); + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + } +} + + +@implementation ASStackTextLayoutSpec +{ + ASStackTextLayoutSpecStyle _textStyle; + std::vector> _children; + std::vector> _stackChildren; + ASDN::RecursiveMutex _propertyLock; +} + ++ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children +{ + ASDisplayNodeAssert(style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone, @"if you don't need baseline alignment, use ASStackLayoutSpec"); + + ASStackTextLayoutSpec *spec = [super new]; + if (spec) { + spec->_textStyle = style; + spec->_children = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + + spec->_children.push_back(child); + spec->_stackChildren.push_back(child); + } + } + return spec; +} + ++ (instancetype)new +{ + ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); +} + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); + + // Alter the positioned layouts to include baselines + const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return baselineForItem(_textStyle, a) < baselineForItem(_textStyle, b); + }); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(_textStyle, *baselineIt); + + CGPoint p = CGPointZero; + BOOL first = YES; + auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ + __weak id textChild = (id) l.layoutableObject; + if (first) { + p = l.position; + } + first = NO; + + if (stackStyle.direction == ASStackLayoutDirectionHorizontal) { + l.position = p + CGPointMake(0, baselineOffset(_textStyle, l, maxBaseline)); + } + + CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + [(id)l.layoutableObject spacingAfter] + spacingAfterBaseline, 0); + + return l; + }); + + const ASStackPositionedLayout alteredPositionedLayouts = {stackedChildren, positionedLayout.crossSize}; + const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, alteredPositionedLayouts.crossSize); + + NSArray *sublayouts = [NSArray arrayWithObjects:&alteredPositionedLayouts.sublayouts[0] count:alteredPositionedLayouts.sublayouts.size()]; + + + return [ASLayout newWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; +} +@end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.h b/AsyncDisplayKit/Private/ASStackPositionedLayout.h index ea9f56fd26..211bda5b11 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.h @@ -17,7 +17,6 @@ struct ASStackPositionedLayout { const std::vector sublayouts; const CGFloat crossSize; - const CGFloat distanceToBaseline; /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 1e667abd73..b567be3ef7 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -18,8 +18,10 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &item) { const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsFirstBaseline || alignItems == ASStackLayoutAlignItemsLastBaseline) { - return [item.child distanceToBaseline:alignItems]; + if (alignItems == ASStackLayoutAlignItemsFirstBaseline) { + return item.child.layoutInsets.top; + } else if (alignItems == ASStackLayoutAlignItemsLastBaseline) { + return item.child.layoutInsets.bottom; } return 0; } @@ -75,11 +77,11 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.layout.size.height - [l.child distanceToBaseline:style.alignItems] : 0; + CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.layoutInsets.bottom : 0; p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 0); return l.layout; }); - return {stackedChildren, crossSize, maxBaseline}; + return {stackedChildren, crossSize}; } ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, From 2d6ddfe32e59c5c4d9de00a11e8735e13f9ed33b Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Thu, 20 Aug 2015 10:38:54 -0700 Subject: [PATCH 089/145] moved baseline alignment to a layout spec. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 14 ++ AsyncDisplayKit/Layout/ASStackLayoutDefines.h | 8 +- .../Layout/ASStackTextLayoutSpec.h | 51 ++++--- .../Layout/ASStackTextLayoutSpec.mm | 140 ++++++------------ .../Private/ASStackPositionedLayout.mm | 30 +--- .../Private/ASStackTextPositionedLayout.h | 26 ++++ .../Private/ASStackTextPositionedLayout.mm | 118 +++++++++++++++ 7 files changed, 243 insertions(+), 144 deletions(-) create mode 100644 AsyncDisplayKit/Private/ASStackTextPositionedLayout.h create mode 100644 AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 29c10b3ca5..83427c715c 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,6 +217,10 @@ 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 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; + 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; @@ -562,6 +566,8 @@ 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 /* ASStackTextPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackTextPositionedLayout.h; sourceTree = ""; }; + 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackTextPositionedLayout.mm; sourceTree = ""; }; 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; @@ -696,7 +702,9 @@ 058D09AD195D04C000B7D73C /* Products */, FD40E2760492F0CAAEAD552D /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 058D09AD195D04C000B7D73C /* Products */ = { isa = PBXGroup; @@ -928,6 +936,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */, + 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -1056,6 +1066,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, + 9C204A641B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1148,6 +1159,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, + 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1423,6 +1435,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, + 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1526,6 +1539,7 @@ 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, + 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h index 39e51b65c8..d7737f7eab 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutDefines.h @@ -44,11 +44,7 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { /** Center children on cross axis */ ASStackLayoutAlignItemsCenter, /** Expand children to fill cross axis */ - ASStackLayoutAlignItemsStretch, - /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ - ASStackLayoutAlignItemsFirstBaseline, - /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ - ASStackLayoutAlignItemsLastBaseline, + ASStackLayoutAlignItemsStretch }; /** @@ -66,6 +62,4 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { ASStackLayoutAlignSelfCenter, /** Expand to fill cross axis */ ASStackLayoutAlignSelfStretch, - - /** Note: All children in a stack must have the same baseline align type */ }; diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h index ab5b80db19..a7b94da03b 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h @@ -1,35 +1,48 @@ -// -// ASStackTextLayoutSpec.h -// AsyncDisplayKit -// -// Created by ricky cancro on 8/19/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 -/** Orientation of children along cross axis */ typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { - ASStackTextLayoutBaselineAlignmentNone, - /** Children align along the first baseline of the stack. Only available for horizontal stack nodes */ - ASStackTextLayoutBaselineAlignmentFirst, - /** Children align along the last baseline of the stack. Only available for horizontal stack nodes */ - ASStackTextLayoutBaselineAlignmentLast, + /** No baseline alignment. This is only valid for a vertical stack */ + ASStackTextLayoutBaselineAlignmentNone, + /** Align all children to the first baseline. This is only valid for a horizontal stack */ + ASStackTextLayoutBaselineAlignmentFirst, + /** 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 */ + ASStackTextLayoutBaselineAlignmentLast, }; typedef struct { - /** Specifies the direction children are stacked in. */ - ASStackLayoutSpecStyle stackLayoutStyle; - - ASStackTextLayoutBaselineAlignment baselineAlignment; + /** Describes how the stack will be laid out */ + ASStackLayoutSpecStyle stackLayoutStyle; + + /** The type of baseline alignment */ + ASStackTextLayoutBaselineAlignment baselineAlignment; } ASStackTextLayoutSpecStyle; -@interface ASStackTextLayoutSpec : ASLayoutSpec + +/** + A specialized version of a stack layout that aligns its children on a baseline. This spec only works with + ASStackTextLayoutable 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 ASStackTextLayoutSpec : ASLayoutSpec /** @param style Specifies how children are laid out. - @param children ASLayoutable children to be positioned. + @param children ASTextLayoutable children to be positioned. */ + (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm index a15f4c6686..67c6942696 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm @@ -1,10 +1,12 @@ -// -// ASStackTextLayoutSpec.m -// AsyncDisplayKit -// -// Created by ricky cancro on 8/19/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 "ASStackTextLayoutSpec.h" #import "ASStackLayoutable.h" @@ -19,109 +21,61 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" +#import "ASStackTextPositionedLayout.h" #import "ASThread.h" -static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, - const ASLayout *layout) { - - __weak id textChild = (id) layout.layoutableObject; - switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentNone: - return 0; - case ASStackTextLayoutBaselineAlignmentFirst: - return textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: - return textChild.descender; - } - -} - -static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, - const ASLayout *l, - const CGFloat maxBaseline) -{ - switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentFirst: - case ASStackTextLayoutBaselineAlignmentLast: - return maxBaseline - baselineForItem(style, l); - case ASStackTextLayoutBaselineAlignmentNone: - return 0; - } -} - @implementation ASStackTextLayoutSpec { - ASStackTextLayoutSpecStyle _textStyle; - std::vector> _children; - std::vector> _stackChildren; - ASDN::RecursiveMutex _propertyLock; + ASStackTextLayoutSpecStyle _textStyle; + std::vector> _stackChildren; + ASDN::RecursiveMutex _propertyLock; } +@synthesize ascender = _ascender; +@synthesize descender = _descender; + + (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children { - ASDisplayNodeAssert(style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone, @"if you don't need baseline alignment, use ASStackLayoutSpec"); - - ASStackTextLayoutSpec *spec = [super new]; - if (spec) { - spec->_textStyle = style; - spec->_children = std::vector>(); - for (id child in children) { - ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); - - spec->_children.push_back(child); - spec->_stackChildren.push_back(child); - } + ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + + ASStackTextLayoutSpec *spec = [super new]; + if (spec) { + spec->_textStyle = style; + spec->_stackChildren = std::vector>(); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + + spec->_stackChildren.push_back(child); } - return spec; + } + return spec; } + (instancetype)new { - ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); + ASDISPLAYNODE_NOT_DESIGNATED_INITIALIZER(); } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; - - - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); - const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - - // Alter the positioned layouts to include baselines - const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return baselineForItem(_textStyle, a) < baselineForItem(_textStyle, b); - }); - const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(_textStyle, *baselineIt); - - CGPoint p = CGPointZero; - BOOL first = YES; - auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; - if (first) { - p = l.position; - } - first = NO; - - if (stackStyle.direction == ASStackLayoutDirectionHorizontal) { - l.position = p + CGPointMake(0, baselineOffset(_textStyle, l, maxBaseline)); - } - - CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + [(id)l.layoutableObject spacingAfter] + spacingAfterBaseline, 0); - - return l; - }); - - const ASStackPositionedLayout alteredPositionedLayouts = {stackedChildren, positionedLayout.crossSize}; - const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, alteredPositionedLayouts.crossSize); - - NSArray *sublayouts = [NSArray arrayWithObjects:&alteredPositionedLayouts.sublayouts[0] count:alteredPositionedLayouts.sublayouts.size()]; - - - return [ASLayout newWithLayoutableObject:self - size:ASSizeRangeClamp(constrainedSize, finalSize) - sublayouts:sublayouts]; + ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); + const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); + const auto baselinePositionedLayout = ASStackTextPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + + const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); + + NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; + + ASDN::MutexLocker l(_propertyLock); + _ascender = baselinePositionedLayout.ascender; + _descender = baselinePositionedLayout.descender; + + + return [ASLayout newWithLayoutableObject:self + size:ASSizeRangeClamp(constrainedSize, finalSize) + sublayouts:sublayouts]; } @end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index b567be3ef7..d106d0618b 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -15,21 +15,9 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" -static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, - const ASStackUnpositionedItem &item) { - const ASStackLayoutAlignItems alignItems = alignment(item.child.alignSelf, style.alignItems); - if (alignItems == ASStackLayoutAlignItemsFirstBaseline) { - return item.child.layoutInsets.top; - } else if (alignItems == ASStackLayoutAlignItemsLastBaseline) { - return item.child.layoutInsets.bottom; - } - return 0; -} - static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, - const CGFloat crossSize, - const CGFloat maxBaseline) + const CGFloat crossSize) { switch (alignment(l.child.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: @@ -39,9 +27,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - case ASStackLayoutAlignItemsLastBaseline: - case ASStackLayoutAlignItemsFirstBaseline: - return maxBaseline - baselineForItem(style, l); + default: + return 0; } } @@ -61,12 +48,6 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style const auto maxCrossSize = crossDimension(style.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // Find the maximum height for the baseline - const auto baselineIt = std::max_element(unpositionedLayout.items.begin(), unpositionedLayout.items.end(), [&](const ASStackUnpositionedItem &a, const ASStackUnpositionedItem &b){ - return baselineForItem(style, a) < baselineForItem(style, b); - }); - const CGFloat maxBaseline = baselineIt == unpositionedLayout.items.end() ? 0 : baselineForItem(style, *baselineIt); - CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ @@ -75,10 +56,9 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, maxBaseline)); + l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - CGFloat spacingAfterBaseline = (style.direction == ASStackLayoutDirectionVertical && style.baselineRelativeArrangement) ? l.child.layoutInsets.bottom : 0; - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter + spacingAfterBaseline, 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/ASStackTextPositionedLayout.h b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h new file mode 100644 index 0000000000..9794e9b807 --- /dev/null +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h @@ -0,0 +1,26 @@ +/* + * 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 "ASLayout.h" +#import "ASDimension.h" +#import "ASStackTextLayoutSpec.h" +#import "ASStackPositionedLayout.h" + +struct ASStackTextPositionedLayout { + 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 ASStackTextPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASStackTextLayoutSpecStyle &textStyle, + const ASSizeRange &constrainedSize); +}; diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm new file mode 100644 index 0000000000..c694a379b0 --- /dev/null +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm @@ -0,0 +1,118 @@ +/* + * 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 "ASStackTextPositionedLayout.h" + +#import "ASLayoutSpecUtilities.h" +#import "ASStackLayoutSpecUtilities.h" + +static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, + const ASLayout *layout) { + + __weak id textChild = (id) layout.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + case ASStackTextLayoutBaselineAlignmentFirst: + return textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return layout.size.height + textChild.descender; + } + +} + +static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, + const ASLayout *l, + const CGFloat maxAscender, + const CGFloat maxBaseline) +{ + if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { + __weak id textChild = (id)l.layoutableObject; + switch (style.baselineAlignment) { + case ASStackTextLayoutBaselineAlignmentFirst: + return maxAscender - textChild.ascender; + case ASStackTextLayoutBaselineAlignmentLast: + return maxBaseline + textChild.descender - textChild.ascender; + case ASStackTextLayoutBaselineAlignmentNone: + return 0; + } + } + return 0; +} + +static CGFloat maxDimensionForLayout(const ASLayout *l, + const ASStackLayoutSpecStyle &style) +{ + CGFloat maxDimension = crossDimension(style.direction, l.size); + style.direction == ASStackLayoutDirectionVertical ? maxDimension += l.position.x : maxDimension += l.position.y; + return maxDimension; +} + +ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASStackTextLayoutSpecStyle &textStyle, + const ASSizeRange &constrainedSize) +{ + ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; + + + // Get the largest distance from the top of the stack to a baseline. This is the baseline we will align to. + const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ + return baselineForItem(textStyle, a) < baselineForItem(textStyle, b); + }); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); + + // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASStackTextLayoutSpec as its ascender. + 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; + }); + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + + CGPoint p = CGPointZero; + BOOL first = YES; + auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ + __weak id textChild = (id) l.layoutableObject; + p = p + directionPoint(stackStyle.direction, textChild.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); + } + first = NO; + + // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y + l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); + + // If we are a vertical stack, add the minDescender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes + CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); + + return l; + }); + + // The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below). + const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), + [&](ASLayout *a, ASLayout *b) { + return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); + }); + 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 CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); + + // find the child with the largest height. Use that child's descender as the descender to pass back to the ASStackTextLayoutSpec. + 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; + + return {stackedChildren, crossSize, maxAscender, minDescender}; +} From 830d39af92896083940475ac4134a09c913d5d40 Mon Sep 17 00:00:00 2001 From: rcancro Date: Thu, 20 Aug 2015 20:43:18 -0700 Subject: [PATCH 090/145] fixed bug for baseline last --- AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm index c694a379b0..f9429ee177 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm @@ -39,7 +39,7 @@ static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, case ASStackTextLayoutBaselineAlignmentFirst: return maxAscender - textChild.ascender; case ASStackTextLayoutBaselineAlignmentLast: - return maxBaseline + textChild.descender - textChild.ascender; + return maxBaseline - baselineForItem(style, l); case ASStackTextLayoutBaselineAlignmentNone: return 0; } @@ -91,7 +91,7 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); - // If we are a vertical stack, add the minDescender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes + // If we are a vertical stack, add the item's descender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); From 5d286930f644ac67374417d005703f447d1da288 Mon Sep 17 00:00:00 2001 From: ricky cancro <@pinterest.com> Date: Fri, 21 Aug 2015 08:54:28 -0700 Subject: [PATCH 091/145] Renamed ASStackText... to ASBaselineStack... --- AsyncDisplayKit.xcodeproj/project.pbxproj | 54 ++++++++++--------- AsyncDisplayKit/ASTextNode.h | 3 +- ...youtSpec.h => ASBaselineStackLayoutSpec.h} | 19 +++---- ...utSpec.mm => ASBaselineStackLayoutSpec.mm} | 20 +++---- .../Layout/ASBaselineStackLayoutable.h | 23 ++++++++ AsyncDisplayKit/Layout/ASStackLayoutable.h | 7 --- ...ut.h => ASBaselineStackPositionedLayout.h} | 8 +-- ....mm => ASBaselineStackPositionedLayout.mm} | 38 ++++++------- 8 files changed, 98 insertions(+), 74 deletions(-) rename AsyncDisplayKit/Layout/{ASStackTextLayoutSpec.h => ASBaselineStackLayoutSpec.h} (70%) rename AsyncDisplayKit/Layout/{ASStackTextLayoutSpec.mm => ASBaselineStackLayoutSpec.mm} (71%) create mode 100644 AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h rename AsyncDisplayKit/Private/{ASStackTextPositionedLayout.h => ASBaselineStackPositionedLayout.h} (72%) rename AsyncDisplayKit/Private/{ASStackTextPositionedLayout.mm => ASBaselineStackPositionedLayout.mm} (76%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 83427c715c..97b20c3f2b 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,14 +217,16 @@ 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 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; - 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */; }; - 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; - 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */; }; + 9C204A641B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; + 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; + 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */; }; + 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; @@ -566,10 +568,11 @@ 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 /* ASStackTextPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackTextPositionedLayout.h; sourceTree = ""; }; - 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackTextPositionedLayout.mm; sourceTree = ""; }; - 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackTextLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h; sourceTree = ""; }; - 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackTextLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm; sourceTree = ""; }; + 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselineStackPositionedLayout.h; sourceTree = ""; }; + 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselineStackPositionedLayout.mm; sourceTree = ""; }; + 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h; sourceTree = ""; }; + 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h; sourceTree = ""; }; + 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineStackLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.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 = ""; }; @@ -936,8 +939,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, - 9C204A621B86349B00313849 /* ASStackTextPositionedLayout.h */, - 9C204A631B86349B00313849 /* ASStackTextPositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */, + 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -960,6 +963,7 @@ children = ( ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, + 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, @@ -983,8 +987,8 @@ ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, - 9C3061041B857EC400D0530B /* ASStackTextLayoutSpec.h */, - 9C3061051B857EC400D0530B /* ASStackTextLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1045,6 +1049,7 @@ 464052201A3F83C40061C0BA /* ASDataController.h in Headers */, 05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, + 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */, 058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */, 058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1066,7 +1071,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, - 9C204A641B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, + 9C204A641B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1093,7 +1098,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, - 9C3061061B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, + 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1159,7 +1164,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, - 9C204A651B86349B00313849 /* ASStackTextPositionedLayout.h in Headers */, + 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1169,7 +1174,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, - 9C3061071B857EC400D0530B /* ASStackTextLayoutSpec.h in Headers */, + 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1213,6 +1218,7 @@ 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, + 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */, 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */, B350624F1B010EFD0018CF92 /* ASDisplayNode+DebugTiming.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, @@ -1435,7 +1441,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, - 9C204A661B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, + 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1474,7 +1480,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, - 9C3061081B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, + 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1536,10 +1542,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9C3061091B857EC400D0530B /* ASStackTextLayoutSpec.mm in Sources */, + 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, - 9C204A671B86349B00313849 /* ASStackTextPositionedLayout.mm in Sources */, + 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 681c2cb71e..d47925ed78 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,6 +7,7 @@ */ #import +#import @protocol ASTextNodeDelegate; @@ -30,7 +31,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/Layout/ASStackTextLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h similarity index 70% rename from AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h rename to AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h index a7b94da03b..df613f42fc 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h @@ -9,15 +9,16 @@ */ #import +#import -typedef NS_ENUM(NSUInteger, ASStackTextLayoutBaselineAlignment) { +typedef NS_ENUM(NSUInteger, ASBaselineStackLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ - ASStackTextLayoutBaselineAlignmentNone, + ASBaselineStackLayoutBaselineAlignmentNone, /** Align all children to the first baseline. This is only valid for a horizontal stack */ - ASStackTextLayoutBaselineAlignmentFirst, + ASBaselineStackLayoutBaselineAlignmentFirst, /** 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 */ - ASStackTextLayoutBaselineAlignmentLast, + ASBaselineStackLayoutBaselineAlignmentLast, }; @@ -26,24 +27,24 @@ typedef struct { ASStackLayoutSpecStyle stackLayoutStyle; /** The type of baseline alignment */ - ASStackTextLayoutBaselineAlignment baselineAlignment; -} ASStackTextLayoutSpecStyle; + ASBaselineStackLayoutBaselineAlignment baselineAlignment; +} ASBaselineStackLayoutSpecStyle; /** A specialized version of a stack layout that aligns its children on a baseline. This spec only works with - ASStackTextLayoutable children. + ASBaselineStackLayoutable 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 ASStackTextLayoutSpec : ASLayoutSpec +@interface ASBaselineStackLayoutSpec : ASLayoutSpec /** @param style Specifies how children are laid out. @param children ASTextLayoutable children to be positioned. */ -+ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children; ++ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children; @end diff --git a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm similarity index 71% rename from AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm rename to AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm index 67c6942696..52d6be3870 100644 --- a/AsyncDisplayKit/Layout/ASStackTextLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm @@ -8,7 +8,7 @@ * */ -#import "ASStackTextLayoutSpec.h" +#import "ASBaselineStackLayoutSpec.h" #import "ASStackLayoutable.h" #import @@ -21,13 +21,13 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" -#import "ASStackTextPositionedLayout.h" +#import "ASBaselineStackPositionedLayout.h" #import "ASThread.h" -@implementation ASStackTextLayoutSpec +@implementation ASBaselineStackLayoutSpec { - ASStackTextLayoutSpecStyle _textStyle; + ASBaselineStackLayoutSpecStyle _textStyle; std::vector> _stackChildren; ASDN::RecursiveMutex _propertyLock; } @@ -35,16 +35,16 @@ @synthesize ascender = _ascender; @synthesize descender = _descender; -+ (instancetype)newWithStyle:(ASStackTextLayoutSpecStyle)style children:(NSArray *)children ++ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children { - ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASStackTextLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASBaselineStackLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); - ASStackTextLayoutSpec *spec = [super new]; + ASBaselineStackLayoutSpec *spec = [super new]; if (spec) { spec->_textStyle = style; spec->_stackChildren = std::vector>(); - for (id child in children) { - ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASStackTextLayoutable)], @"child must conform to ASStackLayoutable"); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineStackLayoutable)], @"child must conform to ASStackLayoutable"); spec->_stackChildren.push_back(child); } @@ -63,7 +63,7 @@ const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - const auto baselinePositionedLayout = ASStackTextPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h new file mode 100644 index 0000000000..e061641c79 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h @@ -0,0 +1,23 @@ +// +// ASBaselineStackLayoutable.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/21/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASStackLayoutable.h" + +@protocol ASBaselineStackLayoutable + +/** + * @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/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index 057ca8b0fd..b23b4fbbdb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -50,10 +50,3 @@ @property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; @end - -@protocol ASStackTextLayoutable - -@property (nonatomic, readwrite) CGFloat ascender; -@property (nonatomic, readwrite) CGFloat descender; - -@end diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h similarity index 72% rename from AsyncDisplayKit/Private/ASStackTextPositionedLayout.h rename to AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h index 9794e9b807..877da2062f 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h @@ -10,17 +10,17 @@ #import "ASLayout.h" #import "ASDimension.h" -#import "ASStackTextLayoutSpec.h" +#import "ASBaselineStackLayoutSpec.h" #import "ASStackPositionedLayout.h" -struct ASStackTextPositionedLayout { +struct ASBaselineStackPositionedLayout { 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 ASStackTextPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASStackTextLayoutSpecStyle &textStyle, + static ASBaselineStackPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineStackLayoutSpecStyle &textStyle, const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm similarity index 76% rename from AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm rename to AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm index f9429ee177..42ab80d339 100644 --- a/AsyncDisplayKit/Private/ASStackTextPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm @@ -8,39 +8,39 @@ * */ -#import "ASStackTextPositionedLayout.h" +#import "ASBaselineStackPositionedLayout.h" #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" -static CGFloat baselineForItem(const ASStackTextLayoutSpecStyle &style, +static CGFloat baselineForItem(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *layout) { - __weak id textChild = (id) layout.layoutableObject; + __weak id textChild = (id) layout.layoutableObject; switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentNone: + case ASBaselineStackLayoutBaselineAlignmentNone: return 0; - case ASStackTextLayoutBaselineAlignmentFirst: + case ASBaselineStackLayoutBaselineAlignmentFirst: return textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: + case ASBaselineStackLayoutBaselineAlignmentLast: return layout.size.height + textChild.descender; } } -static CGFloat baselineOffset(const ASStackTextLayoutSpecStyle &style, +static CGFloat baselineOffset(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *l, const CGFloat maxAscender, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id textChild = (id)l.layoutableObject; + __weak id textChild = (id)l.layoutableObject; switch (style.baselineAlignment) { - case ASStackTextLayoutBaselineAlignmentFirst: + case ASBaselineStackLayoutBaselineAlignmentFirst: return maxAscender - textChild.ascender; - case ASStackTextLayoutBaselineAlignmentLast: + case ASBaselineStackLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); - case ASStackTextLayoutBaselineAlignmentNone: + case ASBaselineStackLayoutBaselineAlignmentNone: return 0; } } @@ -55,8 +55,8 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, return maxDimension; } -ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASStackTextLayoutSpecStyle &textStyle, +ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineStackLayoutSpecStyle &textStyle, const ASSizeRange &constrainedSize) { ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; @@ -68,16 +68,16 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo }); const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); - // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASStackTextLayoutSpec as its ascender. + // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASBaselineStackLayoutSpec as its ascender. 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; + return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; + __weak id textChild = (id) l.layoutableObject; p = p + directionPoint(stackStyle.direction, textChild.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point @@ -108,11 +108,11 @@ ASStackTextPositionedLayout ASStackTextPositionedLayout::compute(const ASStackPo const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // find the child with the largest height. Use that child's descender as the descender to pass back to the ASStackTextLayoutSpec. + // find the child with the largest height. Use that child's descender as the descender to pass back to the ASBaselineStackLayoutSpec. 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 : ((id)(*descenderIt).layoutableObject).descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } From 1424127813f3f6656562ccc7b40dfa5425319e38 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 21 Aug 2015 10:08:15 -0700 Subject: [PATCH 092/145] fixed copyright --- AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h index e061641c79..0911b93908 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h @@ -1,10 +1,10 @@ -// -// ASBaselineStackLayoutable.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/21/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 "ASStackLayoutable.h" From 49ac17816630f93262db3a5bcc15f115c34b77b9 Mon Sep 17 00:00:00 2001 From: rcancro Date: Sat, 22 Aug 2015 18:47:16 -0700 Subject: [PATCH 093/145] huy's comments --- AsyncDisplayKit/ASDisplayNode.mm | 5 -- .../Layout/ASBaselineStackLayoutSpec.h | 1 - .../Layout/ASBaselineStackLayoutSpec.mm | 9 +- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 4 - .../Private/ASBaselineStackPositionedLayout.h | 2 +- .../ASBaselineStackPositionedLayout.mm | 86 ++++++++++++++----- .../Private/ASStackPositionedLayout.mm | 3 - 7 files changed, 70 insertions(+), 40 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 07a546ea85..c02c053e25 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -453,11 +453,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) #pragma mark - -- (CGFloat)distanceToBaseline:(ASStackLayoutAlignItems)baselineAlignmentType -{ - return 0; -} - - (CGSize)measure:(CGSize)constrainedSize { return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h index df613f42fc..789e82f235 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h @@ -30,7 +30,6 @@ typedef struct { ASBaselineStackLayoutBaselineAlignment baselineAlignment; } ASBaselineStackLayoutSpecStyle; - /** A specialized version of a stack layout that aligns its children on a baseline. This spec only works with ASBaselineStackLayoutable children. diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm index 52d6be3870..bfe8a512b7 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm @@ -27,7 +27,7 @@ @implementation ASBaselineStackLayoutSpec { - ASBaselineStackLayoutSpecStyle _textStyle; + ASBaselineStackLayoutSpecStyle _style; std::vector> _stackChildren; ASDN::RecursiveMutex _propertyLock; } @@ -41,7 +41,7 @@ ASBaselineStackLayoutSpec *spec = [super new]; if (spec) { - spec->_textStyle = style; + spec->_style = style; spec->_stackChildren = std::vector>(); for (id child in children) { ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineStackLayoutable)], @"child must conform to ASStackLayoutable"); @@ -59,11 +59,11 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASStackLayoutSpecStyle stackStyle = _textStyle.stackLayoutStyle; + ASStackLayoutSpecStyle stackStyle = _style.stackLayoutStyle; const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _textStyle, constrainedSize); + const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _style, constrainedSize); const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); @@ -73,7 +73,6 @@ _ascender = baselinePositionedLayout.ascender; _descender = baselinePositionedLayout.descender; - return [ASLayout newWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 242d6e3122..6a3e999db5 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -20,10 +20,6 @@ typedef struct { ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis */ 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*/ - BOOL baselineRelativeArrangement; } ASStackLayoutSpecStyle; /** diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h index 877da2062f..8f18216a98 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h @@ -21,6 +21,6 @@ struct ASBaselineStackPositionedLayout { /** Given a positioned layout, computes each child position using baseline alignment. */ static ASBaselineStackPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &textStyle, + const ASBaselineStackLayoutSpecStyle &style, const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm index 42ab80d339..042f8479df 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm @@ -16,14 +16,14 @@ static CGFloat baselineForItem(const ASBaselineStackLayoutSpecStyle &style, const ASLayout *layout) { - __weak id textChild = (id) layout.layoutableObject; + __weak id child = (id) layout.layoutableObject; switch (style.baselineAlignment) { case ASBaselineStackLayoutBaselineAlignmentNone: return 0; case ASBaselineStackLayoutBaselineAlignmentFirst: - return textChild.ascender; + return child.ascender; case ASBaselineStackLayoutBaselineAlignmentLast: - return layout.size.height + textChild.descender; + return layout.size.height + child.descender; } } @@ -34,10 +34,10 @@ static CGFloat baselineOffset(const ASBaselineStackLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id textChild = (id)l.layoutableObject; + __weak id child = (id)l.layoutableObject; switch (style.baselineAlignment) { case ASBaselineStackLayoutBaselineAlignmentFirst: - return maxAscender - textChild.ascender; + return maxAscender - child.ascender; case ASBaselineStackLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineStackLayoutBaselineAlignmentNone: @@ -56,29 +56,58 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, } ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &textStyle, + const ASBaselineStackLayoutSpecStyle &style, const ASSizeRange &constrainedSize) { - ASStackLayoutSpecStyle stackStyle = textStyle.stackLayoutStyle; + ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle; - - // Get the largest distance from the top of the stack to a baseline. This is the baseline we will align to. + /* 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: + + Hello! Why, hello there! How + are you today? + + The first node has a font of size 14, the second a font of size 12. The first node will have a baseline offset of + the ascender of a font of size 14, the second will have a baseline of the ascender of a font of size 12. The first + baseline will be larger so we will keep that as the max baseline. + + However, if were to align from the last baseline we'd find the max baseline by taking the height of node and adding + the font's descender (its negative). In the case of the first node, which is only 1 line, this should be the same value as the ascender. + The second node, however, has a larger height and there will have a larger baseline offset. + */ const auto baselineIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return baselineForItem(textStyle, a) < baselineForItem(textStyle, b); + return baselineForItem(style, a) < baselineForItem(style, b); }); - const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(textStyle, *baselineIt); + const CGFloat maxBaseline = baselineIt == positionedLayout.sublayouts.end() ? 0 : baselineForItem(style, *baselineIt); - // find the largest ascender for all children. This value will be used in offset computation as well as sent back to the ASBaselineStackLayoutSpec as its ascender. + /* + Step 2: Find the max ascender for all of the children. + Imagine 3 nodes aligned horizontally, all with the same text but with font sizes of 12, 14, 16. Because it is has the largest + ascender node with font size of 16 will not need to move, the other two nodes will align to this node's baseline. The offset we will use + for each node is our computed maxAscender - node.ascender. If the 16pt node had an ascender of 10 and the 14pt node + had an ascender of 8, that means we will offset the 14pt node by 2 pts. + + Note: if we are alinging to the last baseline, then we don't need this value in our computation. However, we do want + 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; }); const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + /* + Step 3: Take each child and update its layout position based on the baseline offset. + + If this is a horizontal stack, we take a positioned child and add to its y offset to align it to the maxBaseline of the children. + If this is a vertical stack, we add the child's descender to the location of the next child to position. This will ensure the + spacing between the two nodes is from the baseline, not the bounding box. + + */ CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id textChild = (id) l.layoutableObject; - p = p + directionPoint(stackStyle.direction, textChild.spacingBefore, 0); + __weak id child = (id) l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -88,17 +117,29 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const A } first = NO; - // add the baseline offset. baselineOffset is only valid in the horizontal direction, so we always add to y - l.position = p + CGPointMake(0, baselineOffset(textStyle, l, maxAscender, maxBaseline)); + // Find the difference between this node's baseline and the max baseline of all the children. Add this difference to the child's y position. + l.position = p + CGPointMake(0, baselineOffset(style, l, maxAscender, maxBaseline)); - // If we are a vertical stack, add the item's descender (it is negative) to the spacing after. This will alter the stack spacing to be on baselines instead of bounding boxes - CGFloat spacingAfterBaseline = (stackStyle.direction == ASStackLayoutDirectionVertical) ? textChild.descender : 0; - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + textChild.spacingAfter + spacingAfterBaseline, 0); + // 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) { + spacingAfterBaseline = child.descender; + } + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); return l; }); - // The cross dimension is the max of the childrens' cross dimensions (clamped to our constraint below). + /* + Step 4: Since we have been mucking with positions, there is a chance that our cross size has changed. Imagine a node with a font size of 40 + and another node with a font size of 12 but with multiple lines. We align these nodes to the first baseline, which will be the baseline of the node with + font size of 40 (max ascender). Now, we have to move the node with multiple lines down to the other node's baseline. This node with multiple lines will + extend below the first node farther than it did before aligning the baselines thus increasing the cross size. + + After finding the new cross size, we need to clamp it so that it fits within the constrainted size. + + */ const auto it = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](ASLayout *a, ASLayout *b) { return maxDimensionForLayout(a, stackStyle) < maxDimensionForLayout(b, stackStyle); @@ -108,7 +149,10 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const A const auto maxCrossSize = crossDimension(stackStyle.direction, constrainedSize.max); const CGFloat crossSize = MIN(MAX(minCrossSize, largestChildCrossSize), maxCrossSize); - // find the child with the largest height. Use that child's descender as the descender to pass back to the ASBaselineStackLayoutSpec. + /* + 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. + */ 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; }); diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index d106d0618b..84bbdac502 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -27,12 +27,9 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, case ASStackLayoutAlignItemsStart: case ASStackLayoutAlignItemsStretch: return 0; - default: - return 0; } } - static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, const CGFloat offset, const ASStackUnpositionedLayout &unpositionedLayout, From e9912425137eb8f821d0493db26fefc71767c624 Mon Sep 17 00:00:00 2001 From: rcancro Date: Sat, 22 Aug 2015 19:01:46 -0700 Subject: [PATCH 094/145] removed "Stack" from baseline related layout names --- AsyncDisplayKit.xcodeproj/project.pbxproj | 60 +++++++++---------- AsyncDisplayKit/ASTextNode.h | 4 +- ...ackLayoutSpec.h => ASBaselineLayoutSpec.h} | 20 +++---- ...kLayoutSpec.mm => ASBaselineLayoutSpec.mm} | 20 +++---- ...ackLayoutable.h => ASBaselineLayoutable.h} | 2 +- ...dLayout.h => ASBaselinePositionedLayout.h} | 8 +-- ...ayout.mm => ASBaselinePositionedLayout.mm} | 34 +++++------ 7 files changed, 74 insertions(+), 74 deletions(-) rename AsyncDisplayKit/Layout/{ASBaselineStackLayoutSpec.h => ASBaselineLayoutSpec.h} (70%) rename AsyncDisplayKit/Layout/{ASBaselineStackLayoutSpec.mm => ASBaselineLayoutSpec.mm} (73%) rename AsyncDisplayKit/Layout/{ASBaselineStackLayoutable.h => ASBaselineLayoutable.h} (91%) rename AsyncDisplayKit/Private/{ASBaselineStackPositionedLayout.h => ASBaselinePositionedLayout.h} (72%) rename AsyncDisplayKit/Private/{ASBaselineStackPositionedLayout.mm => ASBaselinePositionedLayout.mm} (85%) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 97b20c3f2b..0809fbf515 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -217,16 +217,16 @@ 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 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; - 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */; }; - 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */; }; - 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */; }; + 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, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; @@ -568,11 +568,11 @@ 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 /* ASBaselineStackPositionedLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBaselineStackPositionedLayout.h; sourceTree = ""; }; - 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBaselineStackPositionedLayout.mm; sourceTree = ""; }; - 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutable.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h; sourceTree = ""; }; - 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineStackLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h; sourceTree = ""; }; - 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineStackLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm; 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 = ""; }; 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,8 +939,8 @@ ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */, ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */, ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */, - 9C204A621B86349B00313849 /* ASBaselineStackPositionedLayout.h */, - 9C204A631B86349B00313849 /* ASBaselineStackPositionedLayout.mm */, + 9C204A621B86349B00313849 /* ASBaselinePositionedLayout.h */, + 9C204A631B86349B00313849 /* ASBaselinePositionedLayout.mm */, ); path = Private; sourceTree = ""; @@ -963,7 +963,7 @@ children = ( ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */, ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */, - 9C204A681B87803A00313849 /* ASBaselineStackLayoutable.h */, + 9C204A681B87803A00313849 /* ASBaselineLayoutable.h */, ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */, ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */, ACF6ED071B17843500DA7C62 /* ASDimension.h */, @@ -987,8 +987,8 @@ ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, - 9C3061041B857EC400D0530B /* ASBaselineStackLayoutSpec.h */, - 9C3061051B857EC400D0530B /* ASBaselineStackLayoutSpec.mm */, + 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, + 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, ); name = Layout; path = ..; @@ -1049,7 +1049,7 @@ 464052201A3F83C40061C0BA /* ASDataController.h in Headers */, 05A6D05A19D0EB64002DD95E /* ASDealloc2MainObject.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, - 9C204A6A1B87803A00313849 /* ASBaselineStackLayoutable.h in Headers */, + 9C204A6A1B87803A00313849 /* ASBaselineLayoutable.h in Headers */, 058D0A47195D05CB00B7D73C /* ASControlNode.h in Headers */, 058D0A48195D05CB00B7D73C /* ASControlNode.m in Headers */, 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, @@ -1071,7 +1071,7 @@ 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, 058D0A53195D05DC00B7D73C /* _ASDisplayLayer.h in Headers */, - 9C204A641B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, + 9C204A641B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, 058D0A54195D05DC00B7D73C /* _ASDisplayLayer.mm in Headers */, 058D0A55195D05DC00B7D73C /* _ASDisplayView.h in Headers */, 058D0A56195D05DC00B7D73C /* _ASDisplayView.mm in Headers */, @@ -1098,7 +1098,7 @@ 058D0A67195D05DC00B7D73C /* NSMutableAttributedString+TextKitAdditions.m in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 205F0E0F1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h in Headers */, - 9C3061061B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, + 9C3061061B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, 058D0A69195D05EC00B7D73C /* _ASAsyncTransaction.m in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, @@ -1164,7 +1164,7 @@ B35061FA1B010EFD0018CF92 /* ASControlNode+Subclasses.h in Headers */, B35062371B010EFD0018CF92 /* ASTextNodeWordKerner.h in Headers */, B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */, - 9C204A651B86349B00313849 /* ASBaselineStackPositionedLayout.h in Headers */, + 9C204A651B86349B00313849 /* ASBaselinePositionedLayout.h in Headers */, B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */, B35061F81B010EFD0018CF92 /* ASControlNode.h in Headers */, 430E7C901B4C23F100697A4C /* ASIndexPath.h in Headers */, @@ -1174,7 +1174,7 @@ B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC76C1B701CED00AD841F /* ASOverlayLayoutSpec.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, - 9C3061071B857EC400D0530B /* ASBaselineStackLayoutSpec.h in Headers */, + 9C3061071B857EC400D0530B /* ASBaselineLayoutSpec.h in Headers */, B35062571B010F070018CF92 /* ASAssert.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, @@ -1218,7 +1218,7 @@ 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */, 34EFC7701B701CFA00AD841F /* ASStackLayoutDefines.h in Headers */, - 9C204A6B1B87803A00313849 /* ASBaselineStackLayoutable.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 */, @@ -1441,7 +1441,7 @@ 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A1E195D050800B7D73C /* ASTextNodeShadower.m in Sources */, - 9C204A661B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, + 9C204A661B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, @@ -1480,7 +1480,7 @@ 0549634A1A1EA066000F8E56 /* ASBasicImageDownloader.mm in Sources */, 058D0A14195D050800B7D73C /* ASDisplayNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, - 9C3061081B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, + 9C3061081B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 058D0A2B195D050800B7D73C /* ASImageNode+CGExtras.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, @@ -1542,10 +1542,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9C3061091B857EC400D0530B /* ASBaselineStackLayoutSpec.mm in Sources */, + 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */, 34EFC7641B701CC600AD841F /* ASCenterLayoutSpec.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, - 9C204A671B86349B00313849 /* ASBaselineStackPositionedLayout.mm in Sources */, + 9C204A671B86349B00313849 /* ASBaselinePositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062311B010EFD0018CF92 /* ASTextNodeRenderer.mm in Sources */, B35062051B010EFD0018CF92 /* ASMultiplexImageNode.mm in Sources */, diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index d47925ed78..b43696a3ff 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,7 +7,7 @@ */ #import -#import +#import @protocol ASTextNodeDelegate; @@ -31,7 +31,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/Layout/ASBaselineStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h similarity index 70% rename from AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h rename to AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 789e82f235..ca59a0d00b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -9,16 +9,16 @@ */ #import -#import +#import -typedef NS_ENUM(NSUInteger, ASBaselineStackLayoutBaselineAlignment) { +typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ - ASBaselineStackLayoutBaselineAlignmentNone, + ASBaselineLayoutBaselineAlignmentNone, /** Align all children to the first baseline. This is only valid for a horizontal stack */ - ASBaselineStackLayoutBaselineAlignmentFirst, + 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 */ - ASBaselineStackLayoutBaselineAlignmentLast, + ASBaselineLayoutBaselineAlignmentLast, }; @@ -27,23 +27,23 @@ typedef struct { ASStackLayoutSpecStyle stackLayoutStyle; /** The type of baseline alignment */ - ASBaselineStackLayoutBaselineAlignment baselineAlignment; -} ASBaselineStackLayoutSpecStyle; + ASBaselineLayoutBaselineAlignment baselineAlignment; +} ASBaselineLayoutSpecStyle; /** A specialized version of a stack layout that aligns its children on a baseline. This spec only works with - ASBaselineStackLayoutable children. + 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 ASBaselineStackLayoutSpec : ASLayoutSpec +@interface ASBaselineLayoutSpec : ASLayoutSpec /** @param style Specifies how children are laid out. @param children ASTextLayoutable children to be positioned. */ -+ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children; ++ (instancetype)newWithStyle:(ASBaselineLayoutSpecStyle)style children:(NSArray *)children; @end diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm similarity index 73% rename from AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm rename to AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index bfe8a512b7..5e1c78d2c6 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -8,7 +8,7 @@ * */ -#import "ASBaselineStackLayoutSpec.h" +#import "ASBaselineLayoutSpec.h" #import "ASStackLayoutable.h" #import @@ -21,13 +21,13 @@ #import "ASStackLayoutSpecUtilities.h" #import "ASStackPositionedLayout.h" #import "ASStackUnpositionedLayout.h" -#import "ASBaselineStackPositionedLayout.h" +#import "ASBaselinePositionedLayout.h" #import "ASThread.h" -@implementation ASBaselineStackLayoutSpec +@implementation ASBaselineLayoutSpec { - ASBaselineStackLayoutSpecStyle _style; + ASBaselineLayoutSpecStyle _style; std::vector> _stackChildren; ASDN::RecursiveMutex _propertyLock; } @@ -35,16 +35,16 @@ @synthesize ascender = _ascender; @synthesize descender = _descender; -+ (instancetype)newWithStyle:(ASBaselineStackLayoutSpecStyle)style children:(NSArray *)children ++ (instancetype)newWithStyle:(ASBaselineLayoutSpecStyle)style children:(NSArray *)children { - ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASBaselineStackLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); + ASDisplayNodeAssert((style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal && style.baselineAlignment != ASBaselineLayoutBaselineAlignmentNone) || style.stackLayoutStyle.direction == ASStackLayoutDirectionVertical, @"baselineAlignment is set to none. If you don't need baseline alignment please use ASStackLayoutSpec"); - ASBaselineStackLayoutSpec *spec = [super new]; + ASBaselineLayoutSpec *spec = [super new]; if (spec) { spec->_style = style; spec->_stackChildren = std::vector>(); - for (id child in children) { - ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineStackLayoutable)], @"child must conform to ASStackLayoutable"); + for (id child in children) { + ASDisplayNodeAssert([child conformsToProtocol:@protocol(ASBaselineLayoutable)], @"child must conform to ASStackLayoutable"); spec->_stackChildren.push_back(child); } @@ -63,7 +63,7 @@ const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); - const auto baselinePositionedLayout = ASBaselineStackPositionedLayout::compute(positionedLayout, _style, constrainedSize); + const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, _style, constrainedSize); const CGSize finalSize = directionSize(stackStyle.direction, unpositionedLayout.stackDimensionSum, baselinePositionedLayout.crossSize); diff --git a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h similarity index 91% rename from AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h rename to AsyncDisplayKit/Layout/ASBaselineLayoutable.h index 0911b93908..5e05029964 100644 --- a/AsyncDisplayKit/Layout/ASBaselineStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h @@ -8,7 +8,7 @@ #import "ASStackLayoutable.h" -@protocol ASBaselineStackLayoutable +@protocol ASBaselineLayoutable /** * @abstract The distance from the top of the layoutable object to its baseline diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h similarity index 72% rename from AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h rename to AsyncDisplayKit/Private/ASBaselinePositionedLayout.h index 8f18216a98..4f023cd5f4 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.h +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.h @@ -10,17 +10,17 @@ #import "ASLayout.h" #import "ASDimension.h" -#import "ASBaselineStackLayoutSpec.h" +#import "ASBaselineLayoutSpec.h" #import "ASStackPositionedLayout.h" -struct ASBaselineStackPositionedLayout { +struct ASBaselinePositionedLayout { 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 ASBaselineStackPositionedLayout compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &style, + static ASBaselinePositionedLayout compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineLayoutSpecStyle &style, const ASSizeRange &constrainedSize); }; diff --git a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm similarity index 85% rename from AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm rename to AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index 042f8479df..2e3d2b3bfe 100644 --- a/AsyncDisplayKit/Private/ASBaselineStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -8,39 +8,39 @@ * */ -#import "ASBaselineStackPositionedLayout.h" +#import "ASBaselinePositionedLayout.h" #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" -static CGFloat baselineForItem(const ASBaselineStackLayoutSpecStyle &style, +static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - __weak id child = (id) layout.layoutableObject; + __weak id child = (id) layout.layoutableObject; switch (style.baselineAlignment) { - case ASBaselineStackLayoutBaselineAlignmentNone: + case ASBaselineLayoutBaselineAlignmentNone: return 0; - case ASBaselineStackLayoutBaselineAlignmentFirst: + case ASBaselineLayoutBaselineAlignmentFirst: return child.ascender; - case ASBaselineStackLayoutBaselineAlignmentLast: + case ASBaselineLayoutBaselineAlignmentLast: return layout.size.height + child.descender; } } -static CGFloat baselineOffset(const ASBaselineStackLayoutSpecStyle &style, +static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const ASLayout *l, const CGFloat maxAscender, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id child = (id)l.layoutableObject; + __weak id child = (id)l.layoutableObject; switch (style.baselineAlignment) { - case ASBaselineStackLayoutBaselineAlignmentFirst: + case ASBaselineLayoutBaselineAlignmentFirst: return maxAscender - child.ascender; - case ASBaselineStackLayoutBaselineAlignmentLast: + case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); - case ASBaselineStackLayoutBaselineAlignmentNone: + case ASBaselineLayoutBaselineAlignmentNone: return 0; } } @@ -55,8 +55,8 @@ static CGFloat maxDimensionForLayout(const ASLayout *l, return maxDimension; } -ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, - const ASBaselineStackLayoutSpecStyle &style, +ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPositionedLayout &positionedLayout, + const ASBaselineLayoutSpecStyle &style, const ASSizeRange &constrainedSize) { ASStackLayoutSpecStyle stackStyle = style.stackLayoutStyle; @@ -91,9 +91,9 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::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 ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; + return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -106,7 +106,7 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::compute(const A CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id child = (id) l.layoutableObject; + __weak id child = (id) l.layoutableObject; p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point @@ -156,7 +156,7 @@ ASBaselineStackPositionedLayout ASBaselineStackPositionedLayout::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 : ((id)(*descenderIt).layoutableObject).descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id)(*descenderIt).layoutableObject).descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } From 499adb514566fd8917febf94461cb6d9803d4ea7 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:04:06 -0700 Subject: [PATCH 095/145] Added method to ASLayoutable to allow a layoutable to override how it will be added to the layoutSpec. Also moved ASStaticLayoutSpec to act more like the other layouts. --- AsyncDisplayKit.xcodeproj/project.pbxproj | 6 +++ AsyncDisplayKit/ASDisplayNode.mm | 5 ++ .../Layout/ASBackgroundLayoutSpec.mm | 25 ++++------ AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h | 3 -- .../Layout/ASBaselineLayoutSpec.mm | 28 ++++++----- AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASLayoutSpec.h | 9 ++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 50 +++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutable.h | 13 +++++ AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 28 +++++------ AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm | 11 +--- AsyncDisplayKit/Layout/ASStackLayoutSpec.h | 3 -- AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 45 +++++++++-------- AsyncDisplayKit/Layout/ASStaticLayoutSpec.h | 41 +-------------- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 47 ++++++----------- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 24 +++++++++ 17 files changed, 191 insertions(+), 169 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASStaticLayoutable.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 0809fbf515..38e68769dc 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -229,6 +229,8 @@ 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -574,6 +576,7 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -985,6 +988,7 @@ AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */, ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */, ACF6ED181B17843500DA7C62 /* ASStaticLayoutSpec.h */, ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, @@ -1038,6 +1042,7 @@ ACF6ED201B17843500DA7C62 /* ASDimension.h in Headers */, ACF6ED2B1B17843500DA7C62 /* ASOverlayLayoutSpec.h in Headers */, ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */, + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */, ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, @@ -1152,6 +1157,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */, 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index ab1a644091..f06fb638bb 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -659,6 +659,11 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible return NO; } +- (id)finalLayoutable +{ + return self; +} + /** * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index fd3cfce134..ea18aca990 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kBackgroundChildKey = @"kBackgroundChildKey"; + @interface ASBackgroundLayoutSpec () -{ - id _child; - id _background; -} @end @implementation ASBackgroundLayoutSpec @@ -30,12 +28,11 @@ } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _child = child; - _background = background; + [self setChild:child]; + self.background = background; return self; } - + (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background; { return [[self alloc] initWithChild:child background:background]; @@ -46,12 +43,12 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2]; - if (_background) { + if (self.background) { // Size background to exactly the same size. - ASLayout *backgroundLayout = [_background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; backgroundLayout.position = CGPointZero; [sublayouts addObject:backgroundLayout]; } @@ -63,14 +60,12 @@ - (void)setBackground:(id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _background = background; + [super setChild:background forIdentifier:kBackgroundChildKey]; } -- (void)setChild:(id)child +- (id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; + return [super childForIdentifier:kBackgroundChildKey]; } @end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 8b784a56c3..46587d996b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -42,9 +42,6 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** The type of baseline alignment */ @property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - /** @param direction The direction of the stack view (horizontal or vertical) @param spacing The spacing between the children diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 670c69c991..45fa687f8c 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -27,7 +27,6 @@ @implementation ASBaselineLayoutSpec { - std::vector> _children; ASDN::RecursiveMutex _propertyLock; } @@ -47,10 +46,7 @@ _justifyContent = justifyContent; _baselineAlignment = baselineAlignment; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } @@ -65,7 +61,12 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, stackStyle, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); @@ -82,16 +83,19 @@ sublayouts:sublayouts]; } -- (void)addChild:(id)child +- (void)setChildren:(NSArray *)children { - _children.push_back(child); + [super setChildren:children]; +#if DEBUG + for (id child in children) { + NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif } -- (void)addChildren:(NSArray *)children +- (void)setChild:(id)child forIdentifier:(NSString *)identifier { - for (id child in children) { - [self addChild:child]; - } + ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); } @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index 1830895320..58dc55f962 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -17,7 +17,6 @@ { ASCenterLayoutSpecCenteringOptions _centeringOptions; ASCenterLayoutSpecSizingOptions _sizingOptions; - id _child; } - (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions @@ -30,7 +29,7 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _centeringOptions = centeringOptions; _sizingOptions = sizingOptions; - _child = child; + [self setChild:child]; return self; } @@ -41,12 +40,6 @@ return [[self alloc] initWithCenteringOptions:centeringOptions sizingOptions:sizingOptions child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -71,7 +64,7 @@ (_centeringOptions & ASCenterLayoutSpecCenteringX) != 0 ? 0 : constrainedSize.min.width, (_centeringOptions & ASCenterLayoutSpecCenteringY) != 0 ? 0 : constrainedSize.min.height, }; - ASLayout *sublayout = [_child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; + ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; // If we have an undetermined height or width, use the child size to define the layout // size diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 5ca96ab848..cc6ec9411a 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -19,7 +19,6 @@ @interface ASInsetLayoutSpec () { UIEdgeInsets _insets; - id _child; } @end @@ -50,7 +49,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _insets = insets; - _child = child; + [self setChild:child]; return self; } @@ -59,12 +58,6 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) return [[self alloc] initWithInsets:insets child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setInsets:(UIEdgeInsets)insets { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -95,7 +88,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) MAX(0, constrainedSize.max.height - insetsY), } }; - ASLayout *sublayout = [_child measureWithSizeRange:insetConstrainedSize]; + ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize]; const CGSize computedSize = ASSizeRangeClamp(constrainedSize, { finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width), diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index bb200cbfe1..f599c84813 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -22,4 +22,13 @@ - (instancetype)init; +- (void)setChild:(id)child; +- (id)child; + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier; +- (id)childForIdentifier:(NSString *)identifier; + +- (void)setChildren:(NSArray *)children; +- (NSArray *)children; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index ef09aa365e..58ecd114e9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,13 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +static NSString * const kDefaultChildKey = @"kDefaultChildKey"; +static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; + +@interface ASLayoutSpec() +@property (nonatomic, strong) NSMutableDictionary *layoutChildren; +@end + @implementation ASLayoutSpec @synthesize spacingBefore = _spacingBefore; @@ -24,12 +31,14 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize layoutChildren = _layoutChildren; - (instancetype)init { if (!(self = [super init])) { return nil; } + _layoutChildren = [NSMutableDictionary dictionary]; _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; @@ -42,4 +51,45 @@ return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } +- (id)finalLayoutable +{ + return self; +} + +- (void)setChild:(id)child; +{ + [self setChild:child forIdentifier:kDefaultChildKey]; +} + +- (id)child +{ + return self.layoutChildren[kDefaultChildKey]; +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + self.layoutChildren[identifier] = [child finalLayoutable]; +} + +- (id)childForIdentifier:(NSString *)identifier +{ + return self.layoutChildren[identifier]; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + [finalChildren addObject:[child finalLayoutable]]; + } + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; +} + +- (NSArray *)children +{ + return self.layoutChildren[kDefaultChildrenKey]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ac01ee05f..4ff1ee030a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -12,6 +12,7 @@ #import @class ASLayout; +@class ASLayoutSpec; /** * The ASLayoutable protocol declares a method for measuring the layout of an object. A class must implement the method @@ -29,4 +30,16 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; +/** + @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before + being added to a ASLayoutSpec. + + For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement + layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method + a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. + + It is rare that a class will need to implement this method. + */ +- (id)finalLayoutable; + @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index f4f025573b..05c1c0c4ca 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kOverlayChildKey = @"kOverlayChildKey"; + @implementation ASOverlayLayoutSpec -{ - id _overlay; - id _child; -} - (instancetype)initWithChild:(id)child overlay:(id)overlay { @@ -26,8 +24,8 @@ return nil; } ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - _overlay = overlay; - _child = child; + self.overlay = overlay; + [self setChild:child]; return self; } @@ -36,16 +34,14 @@ return [[self alloc] initWithChild:child overlay:overlay]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setOverlay:(id)overlay { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _overlay = overlay; + [super setChild:overlay forIdentifier:kOverlayChildKey]; +} + +- (id)overlay +{ + return [super childForIdentifier:kOverlayChildKey]; } /** @@ -56,8 +52,8 @@ ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; - if (_overlay) { - ASLayout *overlayLayout = [_overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + if (self.overlay) { + ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; overlayLayout.position = CGPointZero; [sublayouts addObject:overlayLayout]; } diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 5e5c6f0c66..64e9e2c118 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -22,7 +22,6 @@ @implementation ASRatioLayoutSpec { CGFloat _ratio; - id _child; } + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child @@ -38,16 +37,10 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); _ratio = ratio; - _child = child; + [self setChild:child]; return self; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setRatio:(CGFloat)ratio { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -77,7 +70,7 @@ // If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through. const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize); - ASLayout *sublayout = [_child measureWithSizeRange:childRange]; + ASLayout *sublayout = [self.child measureWithSizeRange:childRange]; sublayout.position = CGPointZero; return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 7825a9b8fc..b5a126c5cb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -55,7 +55,4 @@ */ + (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 3149979dd6..07243f30cc 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -23,9 +23,6 @@ #import "ASThread.h" @implementation ASStackLayoutSpec -{ - std::vector> _children; -} - (instancetype)init { @@ -47,26 +44,10 @@ _spacing = spacing; _justifyContent = justifyContent; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } -- (void)addChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _children.push_back(child); -} - -- (void)addChildren:(NSArray *)children -{ - for (id child in children) { - [self addChild:child]; - } -} - - (void)setDirection:(ASStackLayoutDirection)direction { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -91,10 +72,32 @@ _spacing = spacing; } +- (void)setChildren:(NSArray *)children +{ + [super setChildren:children]; + +#if DEBUG + for (id child in children) { + ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); +} + - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, style, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 38ed2f06d6..8bd3f3d9f2 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -11,43 +11,6 @@ #import #import -/** - * An ASStaticLayoutSpecChild object wraps an ASLayoutable object and provides position and size information, - * to be used as a child of an ASStaticLayoutSpec. - */ -@interface ASStaticLayoutSpecChild : NSObject - -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) id layoutableObject; - -/** - If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. - */ -@property (nonatomic, readonly) ASRelativeSizeRange size; - -/** - * Initializer. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - * - * @param size The size range that this child's size is trstricted according to. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; - -/** - * Convenience initializer with default size is Unconstrained in both dimensions, which sets the child's min size to zero - * and max size to the maximum available space it can consume without overflowing the spec's bounds. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; - -@end - /** * A layout spec that positions children at fixed positions. * @@ -56,10 +19,8 @@ @interface ASStaticLayoutSpec : ASLayoutSpec /** - @param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild. + @param children Children to be positioned at fixed positions, each conforms to ASStaticLayoutable */ + (instancetype)staticLayoutSpecWithChildren:(NSArray *)children; -- (void)addChild:(ASStaticLayoutSpecChild *)child; - @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 61a6d1724e..38a119a79e 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -13,31 +13,9 @@ #import "ASLayoutSpecUtilities.h" #import "ASInternalHelpers.h" #import "ASLayout.h" - -@implementation ASStaticLayoutSpecChild - -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; -{ - ASStaticLayoutSpecChild *c = [[super alloc] init]; - if (c) { - c->_position = position; - c->_layoutableObject = layoutableObject; - c->_size = size; - } - return c; -} - -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject -{ - return [self staticLayoutChildWithPosition:position layoutableObject:layoutableObject size:ASRelativeSizeRangeUnconstrained]; -} - -@end +#import "ASStaticLayoutable.h" @implementation ASStaticLayoutSpec -{ - NSArray *_children; -} + (instancetype)staticLayoutSpecWithChildren:(NSArray *)children { @@ -49,14 +27,19 @@ if (!(self = [super init])) { return nil; } - _children = children; + self.children = children; return self; } -- (void)addChild:(ASStaticLayoutSpecChild *)child +- (void)setChildren:(NSArray *)children { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _children = [_children arrayByAddingObject:child]; + [super setChildren:children]; + +#if DEBUG + for (id child in children) { + ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStaticLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStaticLayoutable)]), @"child must conform to ASStaticLayoutable"); + } +#endif } - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize @@ -66,16 +49,16 @@ constrainedSize.max.height }; - NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:_children.count]; - for (ASStaticLayoutSpecChild *child in _children) { + NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; + for (id child in self.children) { CGSize autoMaxSize = { constrainedSize.max.width - child.position.x, constrainedSize.max.height - child.position.y }; - ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.size) + ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) - : ASRelativeSizeRangeResolve(child.size, size); - ASLayout *sublayout = [child.layoutableObject measureWithSizeRange:childConstraint]; + : ASRelativeSizeRangeResolve(child.sizeRange, size); + ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; sublayout.position = child.position; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h new file mode 100644 index 0000000000..5618fa6e9b --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import +#import + +@protocol ASStaticLayoutable + +/** + If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. + */ +@property (nonatomic, assign) ASRelativeSizeRange sizeRange; + +/** The position of this object within its parent spec. */ +@property (nonatomic, assign) CGPoint position; + +@end From 32e2f7f1ad35b08bfa01310c5387c64851281f0d Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:37:34 -0700 Subject: [PATCH 096/145] fix kittens example --- examples/Kittens/Sample/KittenNode.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index faf2cc9693..d1f49351a2 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -140,7 +140,7 @@ static const CGFloat kInnerPadding = 10.0f; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; stackSpec.spacing = kInnerPadding; - [stackSpec addChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; + [stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding); From 98b41a4b1cb706195623596750275e27cd3183b4 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Wed, 26 Aug 2015 16:15:18 -0700 Subject: [PATCH 097/145] 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 8655102738..cbed7f2bb7 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 098/145] 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 ab1a644091..59d0b7833a 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 099/145] Moved ASLayoutable* properties into ASLayoutOptions class --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++ AsyncDisplayKit/ASDisplayNode.h | 4 +- AsyncDisplayKit/ASDisplayNode.mm | 7 - AsyncDisplayKit/ASTextNode.h | 3 +- AsyncDisplayKit/ASTextNode.mm | 7 - AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h | 4 +- .../Layout/ASBaselineLayoutSpec.mm | 25 +--- AsyncDisplayKit/Layout/ASBaselineLayoutable.h | 4 +- AsyncDisplayKit/Layout/ASLayoutOptions.h | 49 +++++++ AsyncDisplayKit/Layout/ASLayoutOptions.m | 120 ++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.h | 15 ++- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 80 +++++++++--- AsyncDisplayKit/Layout/ASLayoutable.h | 1 + AsyncDisplayKit/Layout/ASStackLayoutSpec.mm | 16 +-- AsyncDisplayKit/Layout/ASStackLayoutable.h | 5 +- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 24 +--- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 3 +- .../Private/ASBaselinePositionedLayout.mm | 29 +++-- .../Private/ASStackPositionedLayout.mm | 8 +- .../Private/ASStackUnpositionedLayout.h | 4 +- .../Private/ASStackUnpositionedLayout.mm | 33 +++-- .../ASCenterLayoutSpecSnapshotTests.mm | 2 +- .../ASStackLayoutSpecSnapshotTests.mm | 99 ++++++++------- Podfile.lock | 2 +- .../Kittens/Sample.xcodeproj/project.pbxproj | 2 + examples/Kittens/Sample/KittenNode.mm | 4 +- 26 files changed, 373 insertions(+), 189 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.h create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 38e68769dc..505370e6e5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -231,6 +231,10 @@ 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -577,6 +581,8 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; + 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -993,6 +999,8 @@ ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */, 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, + 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, ); name = Layout; path = ..; @@ -1060,6 +1068,7 @@ 058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */, 058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */, 1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */, + 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */, 058D0A4B195D05CB00B7D73C /* ASDisplayNode.mm in Headers */, 430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */, 058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */, @@ -1191,6 +1200,7 @@ B35062391B010EFD0018CF92 /* ASThread.h in Headers */, B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */, 34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */, + 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */, 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */, B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */, B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */, @@ -1451,6 +1461,7 @@ ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, @@ -1577,6 +1588,7 @@ B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 974bbe1272..7fcfbd5999 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -13,7 +13,7 @@ #import #import -#import +#import /** * UIView creation block. Used to create the backing view of a new display node. @@ -40,7 +40,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)(); * */ -@interface ASDisplayNode : ASDealloc2MainObject +@interface ASDisplayNode : ASDealloc2MainObject /** @name Initializing a node object */ diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index f06fb638bb..291bd16285 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -41,12 +41,6 @@ @implementation ASDisplayNode -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) @@ -155,7 +149,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block) } _methodOverrides = overrides; - _flexBasis = ASRelativeDimensionUnconstrained; _preferredFrameSize = CGSizeZero; } diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 1b35441c81..e8d5b76154 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -7,7 +7,6 @@ */ #import -#import @protocol ASTextNodeDelegate; @@ -30,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @abstract Draws interactive rich text. @discussion Backed by TextKit. */ -@interface ASTextNode : ASControlNode +@interface ASTextNode : ASControlNode /** @abstract The attributed string to show. diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 7ca68f5177..68f4f1fdab 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -17,7 +17,6 @@ #import #import -#import "ASInternalHelpers.h" #import "ASTextNodeRenderer.h" #import "ASTextNodeShadower.h" @@ -108,9 +107,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) UILongPressGestureRecognizer *_longPressGestureRecognizer; } -@synthesize ascender = _ascender; -@synthesize descender = _descender; - #pragma mark - NSObject - (instancetype)init @@ -359,9 +355,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f) self.isAccessibilityElement = YES; } }); - - _ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - _descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); } #pragma mark - Text Layout diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 46587d996b..d147c76700 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -9,7 +9,7 @@ */ #import -#import +#import typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** No baseline alignment. This is only valid for a vertical stack */ @@ -29,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { If the spec is created with a vertical direction, a child's vertical spacing will be measured from its baseline instead of from the child's bounding box. */ -@interface ASBaselineLayoutSpec : ASLayoutSpec +@interface ASBaselineLayoutSpec : ASLayoutSpec /** Specifies the direction children are stacked in. */ @property (nonatomic, assign) ASStackLayoutDirection direction; diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 45fa687f8c..50139a548b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -9,7 +9,6 @@ */ #import "ASBaselineLayoutSpec.h" -#import "ASStackLayoutable.h" #import #import @@ -26,12 +25,6 @@ @implementation ASBaselineLayoutSpec -{ - ASDN::RecursiveMutex _propertyLock; -} - -@synthesize ascender = _ascender; -@synthesize descender = _descender; - (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { @@ -61,8 +54,8 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } @@ -74,25 +67,11 @@ NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()]; - ASDN::MutexLocker l(_propertyLock); - _ascender = baselinePositionedLayout.ascender; - _descender = baselinePositionedLayout.descender; - return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; -#if DEBUG - for (id child in children) { - NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h index 5e05029964..7e52b2c056 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutable.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutable.h @@ -6,9 +6,9 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "ASStackLayoutable.h" +#import -@protocol ASBaselineLayoutable +@protocol ASBaselineLayoutable /** * @abstract The distance from the top of the layoutable object to its baseline diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h new file mode 100644 index 0000000000..0bdee1ad09 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -0,0 +1,49 @@ +// +// ASLayoutOptions.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import + +@protocol ASLayoutable; + +#import +#import +#import + +@interface ASLayoutOptions : NSObject + +- (instancetype)initWithLayoutable:(id)layoutable; +- (void)setValuesFromLayoutable:(id)layoutable; + +@property (nonatomic, assign) BOOL isMutable; + +#if DEBUG +@property (nonatomic, assign) NSUInteger changeMonitor; +#endif + +#pragma mark - ASStackLayoutable + +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +#pragma mark - ASBaselineLayoutable + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +#pragma mark - ASStaticLayoutable + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint position; + +- (void)setupDefaults; + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m new file mode 100644 index 0000000000..6207ef0d07 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -0,0 +1,120 @@ +// +// ASLayoutOptions.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/27/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptions.h" + +#import +#import +#import "ASInternalHelpers.h" +#import + +@implementation ASLayoutOptions + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; +#if DEBUG + [self addObserver:self forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; +#endif + } + return self; +} + +#if DEBUG ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor +{ + NSMutableSet *keys = [NSMutableSet set]; + unsigned int count; + + objc_property_t *properties = class_copyPropertyList([self class], &count); + for (size_t i = 0; i < count; ++i) { + NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; + + if ([property isEqualToString: @"observableSelf"] == NO) { + [keys addObject: property]; + } + } + free(properties); + + return keys; +} + +#endif + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ +#if DEBUG + if ([keyPath isEqualToString:@"changeMonitor"]) { + ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); + } else +#endif + { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.position = self.position; + + return copy; +} + +#pragma mark - Defaults +- (void)setupDefaults +{ + _flexBasis = ASRelativeDimensionUnconstrained; + _spacingBefore = 0; + _spacingAfter = 0; + _flexGrow = NO; + _flexShrink = NO; + _alignSelf = ASStackLayoutAlignSelfAuto; + + _ascender = 0; + _descender = 0; + + _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + _position = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.position = displayNode.frame.origin; + } +} + + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index f599c84813..dc4f100bb5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -8,10 +8,10 @@ * */ -#import +#import /** A layout spec is an immutable object that describes a layout, loosely inspired by React. */ -@interface ASLayoutSpec : NSObject +@interface ASLayoutSpec : NSObject /** Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a @@ -23,12 +23,15 @@ - (instancetype)init; - (void)setChild:(id)child; -- (id)child; - - (void)setChild:(id)child forIdentifier:(NSString *)identifier; -- (id)childForIdentifier:(NSString *)identifier; - - (void)setChildren:(NSArray *)children; + +- (id)child; +- (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child; ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 58ecd114e9..f277a01bc4 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import + static NSString * const kDefaultChildKey = @"kDefaultChildKey"; static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @@ -25,12 +27,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec -@synthesize spacingBefore = _spacingBefore; -@synthesize spacingAfter = _spacingAfter; -@synthesize flexGrow = _flexGrow; -@synthesize flexShrink = _flexShrink; -@synthesize flexBasis = _flexBasis; -@synthesize alignSelf = _alignSelf; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -39,7 +35,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return nil; } _layoutChildren = [NSMutableDictionary dictionary]; - _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; } @@ -61,15 +56,34 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } -- (id)child -{ - return self.layoutChildren[kDefaultChildKey]; -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - self.layoutChildren[identifier] = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + layoutOptions.isMutable = NO; + self.layoutChildren[identifier] = child; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + } + + [finalChildren addObject:finalLayoutable]; + } + + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; } - (id)childForIdentifier:(NSString *)identifier @@ -77,14 +91,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[identifier]; } -- (void)setChildren:(NSArray *)children +- (id)child { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; - for (id child in children) { - [finalChildren addObject:[child finalLayoutable]]; - } - self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; + return self.layoutChildren[kDefaultChildKey]; } - (NSArray *)children @@ -92,4 +101,35 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } +static Class gLayoutOptionsClass = [ASLayoutOptions class]; ++ (void)setLayoutOptionsClass:(Class)layoutOptionsClass +{ + gLayoutOptionsClass = layoutOptionsClass; +} + ++ (ASLayoutOptions *)optionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; + [layoutOptions setValuesFromLayoutable:child]; + layoutOptions.isMutable = NO; + return layoutOptions; +} + ++ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child +{ + objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + ++ (ASLayoutOptions *)layoutOptionsForChild:(id)child +{ + ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); + if (layoutOptions == nil) { + layoutOptions = [self optionsForChild:child]; + [self associateLayoutOptions:layoutOptions withChild:child]; + } + return objc_getAssociatedObject(child, @selector(setChild:)); +} + + + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ff1ee030a..8ba86fdc1b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -10,6 +10,7 @@ #import #import +#import @class ASLayout; @class ASLayoutSpec; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 07243f30cc..ef109d47ae 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -72,17 +72,6 @@ _spacing = spacing; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; - -#if DEBUG - for (id child in children) { - ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); - } -#endif -} - - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); @@ -91,9 +80,8 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - std::vector> stackChildren = std::vector>(); - for (id child in self.children) { - NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { stackChildren.push_back(child); } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index b23b4fbbdb..e26d0a0210 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -8,9 +8,10 @@ * */ -#import +#import +#import -@protocol ASStackLayoutable +@protocol ASStackLayoutable /** * @abstract Additional space to place before this object in the stacking direction. diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 38a119a79e..31fe00c1a7 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -31,17 +31,6 @@ return self; } -- (void)setChildren:(NSArray *)children -{ - [super setChildren:children]; - -#if DEBUG - for (id child in children) { - ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStaticLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStaticLayoutable)]), @"child must conform to ASStaticLayoutable"); - } -#endif -} - - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { CGSize size = { @@ -50,16 +39,17 @@ }; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; - for (id child in self.children) { + for (id child in self.children) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; CGSize autoMaxSize = { - constrainedSize.max.width - child.position.x, - constrainedSize.max.height - child.position.y + constrainedSize.max.width - layoutOptions.position.x, + constrainedSize.max.height - layoutOptions.position.y }; - ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange) + ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) - : ASRelativeSizeRangeResolve(child.sizeRange, size); + : ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size); ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; - sublayout.position = child.position; + sublayout.position = layoutOptions.position; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index 5618fa6e9b..8e7d74acf3 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -8,10 +8,9 @@ * */ -#import #import -@protocol ASStaticLayoutable +@protocol ASStaticLayoutable /** If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index 2e3d2b3bfe..bf0239c952 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -15,15 +15,14 @@ static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - - __weak id child = (id) layout.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return child.ascender; + return layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + child.descender; + return layout.size.height + layoutOptions.descender; } } @@ -34,10 +33,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - __weak id child = (id)l.layoutableObject; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - child.ascender; + return maxAscender - layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -91,9 +90,11 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - return ((id)a.layoutableObject).ascender < ((id)b.layoutableObject).ascender; + ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; + ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; + return layoutOptionsA.ascender < layoutOptionsB.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id)(*ascenderIt).layoutableObject).ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -106,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - __weak id child = (id) l.layoutableObject; - p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -124,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = child.descender; + spacingAfterBaseline = layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -151,12 +152,12 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi /* Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements - ASBaselineLayoutable and needs an ascender and descender to lay itself out properly. + ASLayoutable and needs an ascender and descender to lay itself out properly. */ const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id)(*descenderIt).layoutableObject).descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 84bbdac502..31ff00b595 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -19,7 +19,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - switch (alignment(l.child.alignSelf, style.alignItems)) { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + switch (alignment(layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -48,14 +49,15 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - p = p + directionPoint(style.direction, l.child.spacingBefore, 0); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h index 93a2efcb20..4112af8e66 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.h @@ -16,7 +16,7 @@ struct ASStackUnpositionedItem { /** The original source child. */ - id child; + id child; /** The proposed layout. */ ASLayout *layout; }; @@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout { const CGFloat violation; /** Given a set of children, computes the unpositioned layouts for those children. */ - static ASStackUnpositionedLayout compute(const std::vector> &children, + static ASStackUnpositionedLayout compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange); }; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index cc3be53d1c..da4000cd5e 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -18,14 +18,15 @@ /** Sizes the child given the parameters specified, and returns the computed layout. */ -static ASLayout *crossChildLayout(const id child, +static ASLayout *crossChildLayout(const id child, const ASStackLayoutSpecStyle style, const CGFloat stackMin, const CGFloat stackMax, const CGFloat crossMin, const CGFloat crossMax) { - const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); // stretched children will have a cross dimension of at least crossMin const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); @@ -75,7 +76,8 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; + const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -111,7 +113,8 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; } } -ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) +ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - return child.flexGrow && child.flexShrink; + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + return layoutOptions.flexGrow && layoutOptions.flexShrink; } /** If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific number then we may avoid the first "intrinsic" size calculation. */ -ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, +ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { @@ -283,7 +287,7 @@ static void flexChildrenAlongStackDimension(std::vector Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and stretched. */ -static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, +static std::vector layoutChildrenAlongUnconstrainedStackDimension(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange, const CGSize size, @@ -292,9 +296,10 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); - return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size)); + return AS::map(children, [&](id child) -> ASStackUnpositionedItem { + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; @@ -312,7 +317,7 @@ static std::vector layoutChildrenAlongUnconstrainedStac }); } -ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, +ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector> &children, const ASStackLayoutSpecStyle &style, const ASSizeRange &sizeRange) { diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 5a7cbd0a92..6ea803ba93 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -94,7 +94,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - foregroundNode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 9b6c3d655d..2e9c2e7802 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -41,8 +41,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - subnode.flexGrow = flex; - subnode.flexShrink = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; + [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; } return subnodes; } @@ -114,7 +114,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - ((ASDisplayNode *)subnodes[1]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; // Width is 75px--that's less than the sum of the widths of the children, which is 100px. static ASSizeRange kSize = {{75, 0}, {75, 150}}; @@ -204,23 +204,25 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; + layoutOptions1.spacingBefore = 10; + layoutOptions2.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0; + layoutOptions1.spacingBefore = 0; + layoutOptions2.spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20; + layoutOptions1.spacingAfter = 10; + layoutOptions2.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0; + layoutOptions1.spacingAfter = 0; + layoutOptions2.spacingAfter = 0; style.spacing = 10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10; + layoutOptions1.spacingBefore = -10; + layoutOptions2.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -236,9 +238,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -254,9 +256,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - child1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - child1.flexGrow = YES; - child1.flexShrink = YES; + ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; + layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); + layoutOptions1.flexGrow = YES; + layoutOptions1.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -271,11 +274,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - subnode1.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -291,7 +294,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - subnode2.alignSelf = ASStackLayoutAlignSelfCenter; + [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -311,9 +314,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -332,9 +335,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -353,9 +356,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -374,9 +377,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -396,9 +399,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; - ((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; - ((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -419,8 +422,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10); + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; + layoutOptions.flexGrow = YES; + layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -439,12 +443,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexGrow = YES; + [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - ((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -460,7 +464,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20); + [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -478,8 +482,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - child2.flexGrow = YES; - child2.flexShrink = YES; + ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; + layoutOptions2.flexGrow = YES; + layoutOptions2.flexShrink = YES; // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. @@ -504,13 +509,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - ((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - ((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - ((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES; + [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES; // A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, // causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px. diff --git a/Podfile.lock b/Podfile.lock index 423c2d2851..119b17c410 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.37.2 +COCOAPODS: 0.35.0 diff --git a/examples/Kittens/Sample.xcodeproj/project.pbxproj b/examples/Kittens/Sample.xcodeproj/project.pbxproj index 293b513d1e..1e08bbe8c1 100644 --- a/examples/Kittens/Sample.xcodeproj/project.pbxproj +++ b/examples/Kittens/Sample.xcodeproj/project.pbxproj @@ -58,7 +58,9 @@ 1A943BF0259746F18D6E423F /* Frameworks */, 1AE410B73DA5C3BD087ACDD7 /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; 05E2128219D4DB510098F589 /* Products */ = { isa = PBXGroup; diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index d1f49351a2..410e4a097a 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -135,7 +135,9 @@ static const CGFloat kInnerPadding = 10.0f; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); - _textNode.flexShrink = YES; + ASLayoutOptions *textNodeOptions = [[ASLayoutOptions alloc] init]; + textNodeOptions.flexShrink = YES; + [ASLayoutSpec associateLayoutOptions:textNodeOptions withChild:_textNode]; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; From 62303ebeecbe1797f878dfcfaa3b74b79f77969f Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Fri, 28 Aug 2015 13:37:39 -0700 Subject: [PATCH 100/145] 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 61a6d1724e..eca2637acd 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 101/145] Hide ASLayoutOptions from the user --- AsyncDisplayKit.xcodeproj/project.pbxproj | 26 +++- AsyncDisplayKit/ASDisplayNode.mm | 11 +- AsyncDisplayKit/Layout/ASLayoutOptions.h | 5 +- AsyncDisplayKit/Layout/ASLayoutOptions.m | 18 ++- .../Layout/ASLayoutOptionsPrivate.h | 30 ++++ .../Layout/ASLayoutOptionsPrivate.mm | 129 ++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.h | 6 +- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 23 ++-- AsyncDisplayKit/Layout/ASLayoutable.h | 29 ++-- AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm | 10 +- AsyncDisplayKit/Layout/ASStaticLayoutable.h | 2 +- .../Private/ASBaselinePositionedLayout.mm | 28 ++-- .../Private/ASDisplayNodeInternal.h | 5 +- AsyncDisplayKit/Private/ASLayoutablePrivate.h | 16 +++ .../Private/ASStackPositionedLayout.mm | 9 +- .../Private/ASStackUnpositionedLayout.mm | 22 ++- .../ASCenterLayoutSpecSnapshotTests.mm | 3 +- .../ASStackLayoutSpecSnapshotTests.mm | 100 +++++++------- examples/Kittens/Sample/KittenNode.mm | 4 +- 19 files changed, 345 insertions(+), 131 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm create mode 100644 AsyncDisplayKit/Private/ASLayoutablePrivate.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 505370e6e5..cc0c1c2600 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -229,12 +229,18 @@ 9C3061091B857EC400D0530B /* ASBaselineLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; + 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -471,7 +477,7 @@ 058D09DD195D050800B7D73C /* ASImageNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageNode.h; sourceTree = ""; }; 058D09DE195D050800B7D73C /* ASImageNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASImageNode.mm; sourceTree = ""; }; 058D09DF195D050800B7D73C /* ASTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTextNode.h; sourceTree = ""; }; - 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 058D09E0195D050800B7D73C /* ASTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASTextNode.mm; sourceTree = ""; }; 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayLayer.h; sourceTree = ""; }; 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASDisplayLayer.mm; sourceTree = ""; }; 058D09E4195D050800B7D73C /* _ASDisplayView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASDisplayView.h; sourceTree = ""; }; @@ -580,9 +586,12 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBaselineLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h; sourceTree = ""; }; 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; - 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; + 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; + 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; + 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Private/ASLayoutablePrivate.h; sourceTree = ""; }; + 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -984,6 +993,7 @@ ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */, ACF6ED111B17843500DA7C62 /* ASLayoutable.h */, + 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */, ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */, ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */, ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */, @@ -1001,6 +1011,8 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, + 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); name = Layout; path = ..; @@ -1077,6 +1089,7 @@ 058D0A4F195D05CB00B7D73C /* ASImageNode.h in Headers */, 058D0A50195D05CB00B7D73C /* ASImageNode.mm in Headers */, 058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */, + 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */, 296A0A2E1A9516B2005ACEAA /* ASBatchFetching.h in Headers */, 058D0A52195D05CB00B7D73C /* ASTextNode.mm in Headers */, 055F1A3819ABD413004DAFF1 /* ASRangeController.h in Headers */, @@ -1118,6 +1131,7 @@ 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, 058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, + 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */, 058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */, 058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, @@ -1170,6 +1184,7 @@ B35062321B010EFD0018CF92 /* ASTextNodeShadower.h in Headers */, 34EFC7651B701CCC00AD841F /* ASRelativeSize.h in Headers */, B35062431B010EFD0018CF92 /* UIView+ASConvenience.h in Headers */, + 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */, B31A241E1B0114FD0016AE7A /* AsyncDisplayKit.h in Headers */, B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */, B35061FB1B010EFD0018CF92 /* ASDisplayNode.h in Headers */, @@ -1257,6 +1272,7 @@ B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, + 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1462,6 +1478,7 @@ 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */, @@ -1573,6 +1590,7 @@ B35062471B010EFD0018CF92 /* ASBatchFetching.m in Sources */, B350624E1B010EFD0018CF92 /* ASDisplayNode+AsyncDisplay.mm in Sources */, 34EFC76F1B701CF700AD841F /* ASRatioLayoutSpec.mm in Sources */, + 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, 34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */, B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */, 34EFC75C1B701BD200AD841F /* ASDimension.mm in Sources */, diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 291bd16285..3c02472fc6 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -9,6 +9,7 @@ #import "ASDisplayNode.h" #import "ASDisplayNode+Subclasses.h" #import "ASDisplayNodeInternal.h" +#import "ASLayoutOptionsPrivate.h" #import @@ -41,6 +42,7 @@ @implementation ASDisplayNode +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize preferredFrameSize = _preferredFrameSize; BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector) @@ -652,11 +654,6 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible return NO; } -- (id)finalLayoutable -{ - return self; -} - /** * NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is * disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined @@ -1834,6 +1831,10 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; } +- (id)finalLayoutable { + return self; +} + @end @implementation ASDisplayNode (Debugging) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 0bdee1ad09..091f078ae9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -16,6 +16,9 @@ @interface ASLayoutOptions : NSObject ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; ++ (Class)defaultLayoutOptionsClass; + - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; @@ -42,7 +45,7 @@ #pragma mark - ASStaticLayoutable @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint position; +@property (nonatomic, readwrite) CGPoint layoutPosition; - (void)setupDefaults; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index 6207ef0d07..df3fc0fb61 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -15,6 +15,18 @@ @implementation ASLayoutOptions +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + return gDefaultLayoutOptionsClass; +} + + - (instancetype)initWithLayoutable:(id)layoutable; { self = [super init]; @@ -79,7 +91,7 @@ copy.descender = self.descender; copy.sizeRange = self.sizeRange; - copy.position = self.position; + copy.layoutPosition = self.layoutPosition; return copy; } @@ -98,7 +110,7 @@ _descender = 0; _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _position = CGPointZero; + _layoutPosition = CGPointZero; } // Do this here instead of in Node/Spec subclasses so that custom specs can set default values @@ -112,7 +124,7 @@ if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.position = displayNode.frame.origin; + self.layoutPosition = displayNode.frame.origin; } } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h new file mode 100644 index 0000000000..f7b7f9b9fc --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -0,0 +1,30 @@ +// +// ASDisplayNode+Layoutable.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import +#import + + +@interface ASDisplayNode() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASDisplayNode(ASLayoutOptions) +@end + +@interface ASLayoutSpec() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASLayoutSpec(ASLayoutOptions) +@end + diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm new file mode 100644 index 0000000000..b0332a8007 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -0,0 +1,129 @@ +// +// ASDisplayNode+Layoutable.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptionsPrivate.h" +#import + + +#define ASLayoutOptionsForwarding \ +- (ASLayoutOptions *)layoutOptions\ +{\ +if (_layoutOptions == nil) {\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ +return _layoutOptions;\ +}\ +\ +- (CGFloat)spacingBefore\ +{\ +return self.layoutOptions.spacingBefore;\ +}\ +\ +- (void)setSpacingBefore:(CGFloat)spacingBefore\ +{\ +self.layoutOptions.spacingBefore = spacingBefore;\ +}\ +\ +- (CGFloat)spacingAfter\ +{\ +return self.layoutOptions.spacingAfter;\ +}\ +\ +- (void)setSpacingAfter:(CGFloat)spacingAfter\ +{\ +self.layoutOptions.spacingAfter = spacingAfter;\ +}\ +\ +- (BOOL)flexGrow\ +{\ +return self.layoutOptions.flexGrow;\ +}\ +\ +- (void)setFlexGrow:(BOOL)flexGrow\ +{\ +self.layoutOptions.flexGrow = flexGrow;\ +}\ +\ +- (BOOL)flexShrink\ +{\ +return self.layoutOptions.flexShrink;\ +}\ +\ +- (void)setFlexShrink:(BOOL)flexShrink\ +{\ +self.layoutOptions.flexShrink = flexShrink;\ +}\ +\ +- (ASRelativeDimension)flexBasis\ +{\ +return self.layoutOptions.flexBasis;\ +}\ +\ +- (void)setFlexBasis:(ASRelativeDimension)flexBasis\ +{\ +self.layoutOptions.flexBasis = flexBasis;\ +}\ +\ +- (ASStackLayoutAlignSelf)alignSelf\ +{\ +return self.layoutOptions.alignSelf;\ +}\ +\ +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\ +{\ + self.layoutOptions.alignSelf = alignSelf;\ +}\ +\ +- (CGFloat)ascender\ +{\ + return self.layoutOptions.ascender;\ +}\ +\ +- (void)setAscender:(CGFloat)ascender\ +{\ + self.layoutOptions.ascender = ascender;\ +}\ +\ +- (CGFloat)descender\ +{\ + return self.layoutOptions.descender;\ +}\ +\ +- (void)setDescender:(CGFloat)descender\ +{\ + self.layoutOptions.descender = descender;\ +}\ +\ +- (ASRelativeSizeRange)sizeRange\ +{\ + return self.layoutOptions.sizeRange;\ +}\ +\ +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\ +{\ + self.layoutOptions.sizeRange = sizeRange;\ +}\ +\ +- (CGPoint)layoutPosition\ +{\ + return self.layoutOptions.layoutPosition;\ +}\ +\ +- (void)setLayoutPosition:(CGPoint)position\ +{\ + self.layoutOptions.layoutPosition = position;\ +}\ + + +@implementation ASDisplayNode(ASLayoutOptions) +ASLayoutOptionsForwarding +@end + +@implementation ASLayoutSpec(ASLayoutOptions) +ASLayoutOptionsForwarding +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index dc4f100bb5..494d5d1198 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,8 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; +//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; +//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; +//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index f277a01bc4..994ad8428e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -15,6 +15,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import "ASLayoutOptions.h" +#import "ASLayoutOptionsPrivate.h" #import @@ -27,6 +29,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -71,16 +74,20 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + if ([child respondsToSelector:@selector(finalLayoutable)]) { + id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; + } + } else { + [finalChildren addObject:child]; } - [finalChildren addObject:finalLayoutable]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 8ba86fdc1b..2ea76f3983 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -9,8 +9,10 @@ */ #import +#import #import -#import + +#import @class ASLayout; @class ASLayoutSpec; @@ -20,7 +22,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -31,16 +33,17 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -/** - @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before - being added to a ASLayoutSpec. - - For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement - layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method - a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. - - It is rare that a class will need to implement this method. - */ -- (id)finalLayoutable; +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 31fe00c1a7..8e97c9b42d 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -11,6 +11,8 @@ #import "ASStaticLayoutSpec.h" #import "ASLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" +#import "ASLayoutOptionsPrivate.h" #import "ASInternalHelpers.h" #import "ASLayout.h" #import "ASStaticLayoutable.h" @@ -40,16 +42,16 @@ NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count]; for (id child in self.children) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + ASLayoutOptions *layoutOptions = child.layoutOptions; CGSize autoMaxSize = { - constrainedSize.max.width - layoutOptions.position.x, - constrainedSize.max.height - layoutOptions.position.y + constrainedSize.max.width - layoutOptions.layoutPosition.x, + constrainedSize.max.height - layoutOptions.layoutPosition.y }; ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange) ? ASSizeRangeMake({0, 0}, autoMaxSize) : ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size); ASLayout *sublayout = [child measureWithSizeRange:childConstraint]; - sublayout.position = layoutOptions.position; + sublayout.position = layoutOptions.layoutPosition; [sublayouts addObject:sublayout]; } diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index 8e7d74acf3..f2e565a7a9 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -18,6 +18,6 @@ @property (nonatomic, assign) ASRelativeSizeRange sizeRange; /** The position of this object within its parent spec. */ -@property (nonatomic, assign) CGPoint position; +@property (nonatomic, assign) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index bf0239c952..bf01ff179e 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -12,17 +12,19 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; + + __weak id child = layout.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return layoutOptions.ascender; + return child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + layoutOptions.descender; + return layout.size.height + child.layoutOptions.descender; } } @@ -33,10 +35,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + __weak id child = l.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - layoutOptions.ascender; + return maxAscender - child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -90,11 +92,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; - ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; - return layoutOptionsA.ascender < layoutOptionsB.ascender; + return a.layoutableObject.layoutOptions.ascender < b.layoutableObject.layoutOptions.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.layoutOptions.ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -107,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; - p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); + __weak id child = l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -125,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = layoutOptions.descender; + spacingAfterBaseline = child.layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -157,7 +157,7 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.layoutOptions.descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 5a8883ed02..d727bc5bab 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -17,6 +17,7 @@ #import "ASDisplayNode.h" #import "ASSentinel.h" #import "ASThread.h" +#import "ASLayoutOptions.h" BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); @@ -71,7 +72,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { NSMutableSet *_pendingDisplayNodes; _ASPendingState *_pendingViewState; - + struct { // public properties unsigned synchronous:1; @@ -153,6 +154,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; +- (id)finalLayoutable; + @end @interface UIView (ASDisplayNodeInternal) diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h new file mode 100644 index 0000000000..360c91570b --- /dev/null +++ b/AsyncDisplayKit/Private/ASLayoutablePrivate.h @@ -0,0 +1,16 @@ +// +// ASLayoutablePrivate.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 31ff00b595..95b445043c 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -14,13 +14,13 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" +#import "ASLayoutOptions.h" static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - switch (alignment(layoutOptions.alignSelf, style.alignItems)) { + switch (alignment(l.child.layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -49,15 +49,14 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, l.child.layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index da4000cd5e..7514507a01 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -14,6 +14,7 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" /** Sizes the child given the parameters specified, and returns the computed layout. @@ -25,8 +26,7 @@ static ASLayout *crossChildLayout(const id child, const CGFloat crossMin, const CGFloat crossMax) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(child.layoutOptions.alignSelf, style.alignItems); // stretched children will have a cross dimension of at least crossMin const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); @@ -76,8 +76,7 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(l.child.layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -113,8 +112,7 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexShrink; }; } } ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - return layoutOptions.flexGrow && layoutOptions.flexShrink; + return child.layoutOptions.flexGrow && child.layoutOptions.flexShrink; } /** @@ -297,9 +294,8 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 6ea803ba93..45e167afd7 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -13,6 +13,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASCenterLayoutSpec.h" #import "ASStackLayoutSpec.h" +#import "ASLayoutOptions.h" static const ASSizeRange kSize = {{100, 120}, {320, 160}}; @@ -94,7 +95,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; + foregroundNode.layoutOptions.flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 2e9c2e7802..fbd893940b 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -15,6 +15,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASRatioLayoutSpec.h" #import "ASInsetLayoutSpec.h" +#import "ASLayoutOptions.h" @interface ASStackLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase @end @@ -41,8 +42,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; - [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; + subnode.layoutOptions.flexGrow = flex; + subnode.layoutOptions.flexShrink = flex; } return subnodes; } @@ -114,7 +115,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; + ((ASDisplayNode *)subnodes[1]).layoutOptions.flexShrink = YES; // Width is 75px--that's less than the sum of the widths of the children, which is 100px. static ASSizeRange kSize = {{75, 0}, {75, 150}}; @@ -204,25 +205,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; - layoutOptions1.spacingBefore = 10; - layoutOptions2.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - layoutOptions1.spacingBefore = 0; - layoutOptions2.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 0; - layoutOptions1.spacingAfter = 10; - layoutOptions2.spacingAfter = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - layoutOptions1.spacingAfter = 0; - layoutOptions2.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 0; style.spacing = 10; - layoutOptions1.spacingBefore = -10; - layoutOptions2.spacingAfter = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -238,9 +237,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -256,10 +255,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; - layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - layoutOptions1.flexGrow = YES; - layoutOptions1.flexShrink = YES; + child1.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(1); + child1.layoutOptions.flexGrow = YES; + child1.layoutOptions.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -274,11 +272,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; + subnode1.layoutOptions.flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; + subnode2.layoutOptions.flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -294,7 +292,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; + subnode2.layoutOptions.alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -314,9 +312,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -335,9 +333,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -356,9 +354,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -377,9 +375,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -399,9 +397,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -422,9 +420,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; - layoutOptions.flexGrow = YES; - layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); + subnode.layoutOptions.flexGrow = YES; + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -443,12 +440,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; + subnode.layoutOptions.flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -464,7 +461,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -482,9 +479,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; - layoutOptions2.flexGrow = YES; - layoutOptions2.flexShrink = YES; + child2.layoutOptions.flexGrow = YES; + child2.layoutOptions.flexShrink = YES; // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. @@ -509,13 +505,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.flexShrink = YES; // A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, // causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px. diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 410e4a097a..d1f49351a2 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -135,9 +135,7 @@ static const CGFloat kInnerPadding = 10.0f; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); - ASLayoutOptions *textNodeOptions = [[ASLayoutOptions alloc] init]; - textNodeOptions.flexShrink = YES; - [ASLayoutSpec associateLayoutOptions:textNodeOptions withChild:_textNode]; + _textNode.flexShrink = YES; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; stackSpec.direction = ASStackLayoutDirectionHorizontal; From e6a07ffe586d7431c83750db32e7dd59b5ff3a14 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 15:55:41 -0700 Subject: [PATCH 102/145] Kittens sample working --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 +++++------ AsyncDisplayKit/Layout/ASLayoutOptions.h | 16 +++++++------- AsyncDisplayKit/Layout/ASLayoutOptions.m | 21 ++++++++++++------- .../Layout/ASLayoutOptionsPrivate.h | 18 +++++++++------- .../Layout/ASLayoutOptionsPrivate.mm | 20 ++++++++++-------- AsyncDisplayKit/Layout/ASLayoutSpec.h | 4 ---- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 19 +++++++---------- AsyncDisplayKit/Layout/ASLayoutable.h | 18 ++++------------ AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 19 +++++++++++++++++ AsyncDisplayKit/Private/ASLayoutablePrivate.h | 16 -------------- 10 files changed, 82 insertions(+), 81 deletions(-) create mode 100644 AsyncDisplayKit/Layout/ASLayoutablePrivate.h delete mode 100644 AsyncDisplayKit/Private/ASLayoutablePrivate.h diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index cc0c1c2600..07f1563cf4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -237,10 +237,10 @@ 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; 9C5FA3601B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; - 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -590,8 +590,8 @@ 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; - 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Private/ASLayoutablePrivate.h; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; + 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = ""; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = ""; }; @@ -993,7 +993,7 @@ ACF6ED0B1B17843500DA7C62 /* ASLayout.h */, ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */, ACF6ED111B17843500DA7C62 /* ASLayoutable.h */, - 9C5FA3611B91007100A62714 /* ASLayoutablePrivate.h */, + 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */, ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */, ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */, ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */, @@ -1131,7 +1131,7 @@ 058D0A6B195D05EC00B7D73C /* _ASAsyncTransactionContainer.h in Headers */, 058D0A6C195D05EC00B7D73C /* _ASAsyncTransactionContainer.m in Headers */, 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */, - 9C5FA3631B91007100A62714 /* ASLayoutablePrivate.h in Headers */, + 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, 058D0A6D195D05EC00B7D73C /* _ASAsyncTransactionGroup.h in Headers */, 058D0A6E195D05EC00B7D73C /* _ASAsyncTransactionGroup.m in Headers */, 205F0E1D1B373A2C007741D0 /* ASCollectionViewLayoutController.h in Headers */, @@ -1272,7 +1272,7 @@ B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062191B010EFD0018CF92 /* ASDealloc2MainObject.h in Headers */, B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */, - 9C5FA3641B91007100A62714 /* ASLayoutablePrivate.h in Headers */, + 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 091f078ae9..3df0f7c673 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index df3fc0fb61..a383d8497e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptions.h" @@ -23,6 +25,11 @@ static Class gDefaultLayoutOptionsClass = nil; + (Class)defaultLayoutOptionsClass { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + }); return gDefaultLayoutOptionsClass; } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h index f7b7f9b9fc..b00e09580a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import #import @@ -13,6 +15,7 @@ @interface ASDisplayNode() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end @@ -22,6 +25,7 @@ @interface ASLayoutSpec() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index b0332a8007..431565bde1 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptionsPrivate.h" #import @@ -13,9 +15,9 @@ #define ASLayoutOptionsForwarding \ - (ASLayoutOptions *)layoutOptions\ {\ -if (_layoutOptions == nil) {\ +dispatch_once(&_layoutOptionsInitializeToken, ^{\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ -}\ +});\ return _layoutOptions;\ }\ \ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 494d5d1198..f132d5d7f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,4 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 994ad8428e..39487fe1ec 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -74,20 +74,17 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - if ([child respondsToSelector:@selector(finalLayoutable)]) { - id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; } else { [finalChildren addObject:child]; } - } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 2ea76f3983..38644c0b60 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -11,6 +11,9 @@ #import #import #import +#import +#import +#import #import @@ -22,7 +25,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -33,17 +36,4 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -@property (nonatomic, readwrite) CGFloat spacingBefore; -@property (nonatomic, readwrite) CGFloat spacingAfter; -@property (nonatomic, readwrite) BOOL flexGrow; -@property (nonatomic, readwrite) BOOL flexShrink; -@property (nonatomic, readwrite) ASRelativeDimension flexBasis; -@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; - -@property (nonatomic, readwrite) CGFloat ascender; -@property (nonatomic, readwrite) CGFloat descender; - -@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint layoutPosition; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h new file mode 100644 index 0000000000..5091a30954 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h deleted file mode 100644 index 360c91570b..0000000000 --- a/AsyncDisplayKit/Private/ASLayoutablePrivate.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASLayoutablePrivate.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// -#import - -@class ASLayoutSpec; -@class ASLayoutOptions; - -@protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; -@end From 2155054c69bd4872cbc814f05920fe26b8838818 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:49:40 -0700 Subject: [PATCH 103/145] wasn't copying layout options to final layoutable. --- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 39487fe1ec..a1ba8ed80a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -59,12 +59,25 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } +- (id)layoutableToAddFromLayoutable:(id)child +{ + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + return finalLayoutable; + } + return child; +} + - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - layoutOptions.isMutable = NO; - self.layoutChildren[identifier] = child; + self.layoutChildren[identifier] = [self layoutableToAddFromLayoutable:child];; } - (void)setChildren:(NSArray *)children @@ -73,18 +86,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } else { - [finalChildren addObject:child]; - } + [finalChildren addObject:[self layoutableToAddFromLayoutable:child]]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; From c1fef24c8adb6c836fb2f587cca9578bba2113f3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:51:15 -0700 Subject: [PATCH 104/145] bug in setting the ASLayoutOptions default class. --- AsyncDisplayKit/Layout/ASLayoutOptions.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index a383d8497e..e0ebf622f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -27,8 +27,10 @@ static Class gDefaultLayoutOptionsClass = nil; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } }); return gDefaultLayoutOptionsClass; } From f6d67689854488f158fe91c007c08d7c9ca19ac1 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:11:19 -0700 Subject: [PATCH 105/145] Fixed infinite recursion in finalLayoutable, removed child properties from ASLayoutSpecs --- AsyncDisplayKit/ASDisplayNode.mm | 5 ++- .../Layout/ASBackgroundLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASCenterLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASInsetLayoutSpec.h | 1 - .../Layout/ASLayoutOptionsPrivate.mm | 7 ++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 42 +++---------------- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 4 +- AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h | 1 - AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm | 2 +- AsyncDisplayKit/Layout/ASRatioLayoutSpec.h | 2 - .../Private/ASDisplayNodeInternal.h | 2 - 11 files changed, 19 insertions(+), 49 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 3c02472fc6..d202ceedf0 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -1831,8 +1831,9 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer, return !self.layerBacked && [self.view canPerformAction:action withSender:sender]; } -- (id)finalLayoutable { - return self; +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec +{ + return nil; } @end diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h index ecc2e48473..cab51ea8b3 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASBackgroundLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id background; /** diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index dc50b265e7..37ba24e7e7 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -39,7 +39,6 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { @property (nonatomic, assign) ASCenterLayoutSpecCenteringOptions centeringOptions; @property (nonatomic, assign) ASCenterLayoutSpecSizingOptions sizingOptions; -@property (nonatomic, strong) id child; /** * Initializer. diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h index 3b140dfcc7..ab0a6f106f 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h @@ -29,7 +29,6 @@ */ @interface ASInsetLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, assign) UIEdgeInsets insets; /** diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index 431565bde1..f04c010936 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -16,11 +16,18 @@ - (ASLayoutOptions *)layoutOptions\ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ +if (_layoutOptions == nil) {\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ });\ return _layoutOptions;\ }\ \ +- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ +{\ + _layoutOptions = layoutOptions;\ +}\ +\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index a1ba8ed80a..904c30cc69 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -49,9 +49,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } -- (id)finalLayoutable +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; { - return self; + return nil; } - (void)setChild:(id)child; @@ -61,14 +61,14 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [child layoutOptions]; layoutOptions.isMutable = NO; - if (finalLayoutable != child) { + id finalLayoutable = [child finalLayoutableWithParent:self]; + if (finalLayoutable) { ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + finalLayoutable.layoutOptions = finalLayoutOptions; return finalLayoutable; } return child; @@ -107,35 +107,5 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } -static Class gLayoutOptionsClass = [ASLayoutOptions class]; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass -{ - gLayoutOptionsClass = layoutOptionsClass; -} - -+ (ASLayoutOptions *)optionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; - [layoutOptions setValuesFromLayoutable:child]; - layoutOptions.isMutable = NO; - return layoutOptions; -} - -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child -{ - objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); - if (layoutOptions == nil) { - layoutOptions = [self optionsForChild:child]; - [self associateLayoutOptions:layoutOptions withChild:child]; - } - return objc_getAssociatedObject(child, @selector(setChild:)); -} - - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 5091a30954..c7ab3b7ad3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -14,6 +14,6 @@ @class ASLayoutOptions; @protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; +@property (nonatomic, strong) ASLayoutOptions *layoutOptions; @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h index 35a9577dde..05e53d92e8 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASOverlayLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id overlay; + (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay; diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 05c1c0c4ca..b71375f5be 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -49,7 +49,7 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; if (self.overlay) { diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h index fd7f6d657b..2affa56a75 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h @@ -31,8 +31,6 @@ **/ @interface ASRatioLayoutSpec : ASLayoutSpec - -@property (nonatomic, strong) id child; @property (nonatomic, assign) CGFloat ratio; + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index d727bc5bab..cff5d76461 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -154,8 +154,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; -- (id)finalLayoutable; - @end @interface UIView (ASDisplayNodeInternal) From b84cca0d4ea4582b30b5bb29c4b7cad4b96f35b4 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:14:09 -0700 Subject: [PATCH 106/145] didn't mean to commit this --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 119b17c410..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.35.0 +COCOAPODS: 0.37.2 From b07078fed62c06c595ece807aceba3c73c62bb16 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:41:40 -0700 Subject: [PATCH 107/145] Make layoutOptions readonly --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 9 +++- AsyncDisplayKit/Layout/ASLayoutOptions.m | 44 +++++++++++-------- .../Layout/ASLayoutOptionsPrivate.mm | 7 +-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 +-- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 2 +- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 3df0f7c673..937f6c7640 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -24,6 +24,14 @@ - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; +#pragma mark - Subclasses should implement these! ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor; +- (void)setupDefaults; +- (instancetype)copyWithZone:(NSZone *)zone; +- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; + +#pragma mark - Mutability checks + @property (nonatomic, assign) BOOL isMutable; #if DEBUG @@ -49,6 +57,5 @@ @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; @property (nonatomic, readwrite) CGPoint layoutPosition; -- (void)setupDefaults; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index e0ebf622f5..9d8d9c6a0e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -13,7 +13,6 @@ #import #import #import "ASInternalHelpers.h" -#import @implementation ASLayoutOptions @@ -35,6 +34,10 @@ static Class gDefaultLayoutOptionsClass = nil; return gDefaultLayoutOptionsClass; } +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} - (instancetype)initWithLayoutable:(id)layoutable; { @@ -42,30 +45,31 @@ static Class gDefaultLayoutOptionsClass = nil; if (self) { [self setupDefaults]; [self setValuesFromLayoutable:layoutable]; + _isMutable = YES; #if DEBUG - [self addObserver:self forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; + [self addObserver:self + forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; #endif } return self; } +- (void)dealloc +{ +#if DEBUG + [self removeObserver:self forKeyPath:@"changeMonitor"]; +#endif +} + #if DEBUG + (NSSet *)keyPathsForValuesAffectingChangeMonitor { NSMutableSet *keys = [NSMutableSet set]; - unsigned int count; - - objc_property_t *properties = class_copyPropertyList([self class], &count); - for (size_t i = 0; i < count; ++i) { - NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; - - if ([property isEqualToString: @"observableSelf"] == NO) { - [keys addObject: property]; - } - } - free(properties); + [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; + [keys addObjectsFromArray:@[@"ascender", @"descender"]]; + [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; return keys; } @@ -89,7 +93,12 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ copy.flexBasis = self.flexBasis; copy.spacingAfter = self.spacingAfter; copy.spacingBefore = self.spacingBefore; @@ -98,13 +107,12 @@ static Class gDefaultLayoutOptionsClass = nil; copy.ascender = self.ascender; copy.descender = self.descender; - + copy.sizeRange = self.sizeRange; copy.layoutPosition = self.layoutPosition; - - return copy; } + #pragma mark - Defaults - (void)setupDefaults { diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index f04c010936..39d129ba11 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -17,17 +17,12 @@ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ if (_layoutOptions == nil) {\ -_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] initWithLayoutable:self];\ }\ });\ return _layoutOptions;\ }\ \ -- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ -{\ - _layoutOptions = layoutOptions;\ -}\ -\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 904c30cc69..6c506b655c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -66,9 +66,8 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - finalLayoutable.layoutOptions = finalLayoutOptions; + [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; + finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index c7ab3b7ad3..2d317fc7ca 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -15,5 +15,5 @@ @protocol ASLayoutablePrivate - (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; -@property (nonatomic, strong) ASLayoutOptions *layoutOptions; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; @end From bca7d838e19187fd5412eecd24db1c09dcb54c05 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 31 Aug 2015 14:19:27 -0700 Subject: [PATCH 108/145] making ASLayoutOptions threadsafe --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 10 - AsyncDisplayKit/Layout/ASLayoutOptions.m | 149 ------------- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 250 ++++++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 - 4 files changed, 250 insertions(+), 161 deletions(-) delete mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.m create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.mm diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 937f6c7640..022bbb61f3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -25,19 +25,9 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor; - (void)setupDefaults; -- (instancetype)copyWithZone:(NSZone *)zone; - (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; -#pragma mark - Mutability checks - -@property (nonatomic, assign) BOOL isMutable; - -#if DEBUG -@property (nonatomic, assign) NSUInteger changeMonitor; -#endif - #pragma mark - ASStackLayoutable @property (nonatomic, readwrite) CGFloat spacingBefore; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m deleted file mode 100644 index 9d8d9c6a0e..0000000000 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import "ASLayoutOptions.h" - -#import -#import -#import "ASInternalHelpers.h" - -@implementation ASLayoutOptions - -static Class gDefaultLayoutOptionsClass = nil; -+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass -{ - gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; -} - -+ (Class)defaultLayoutOptionsClass -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (gDefaultLayoutOptionsClass == nil) { - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; - } - }); - return gDefaultLayoutOptionsClass; -} - -- (instancetype)init -{ - return [self initWithLayoutable:nil]; -} - -- (instancetype)initWithLayoutable:(id)layoutable; -{ - self = [super init]; - if (self) { - [self setupDefaults]; - [self setValuesFromLayoutable:layoutable]; - _isMutable = YES; -#if DEBUG - [self addObserver:self - forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; -#endif - } - return self; -} - -- (void)dealloc -{ -#if DEBUG - [self removeObserver:self forKeyPath:@"changeMonitor"]; -#endif -} - -#if DEBUG -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor -{ - NSMutableSet *keys = [NSMutableSet set]; - [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; - [keys addObjectsFromArray:@[@"ascender", @"descender"]]; - [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; - - return keys; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ -#if DEBUG - if ([keyPath isEqualToString:@"changeMonitor"]) { - ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); - } else -#endif - { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - - -#pragma mark - NSCopying -- (id)copyWithZone:(NSZone *)zone -{ - ASLayoutOptions *copy = [[[self class] alloc] init]; - [self copyIntoOptions:copy]; - return copy; -} - -- (void)copyIntoOptions:(ASLayoutOptions *)copy -{ - copy.flexBasis = self.flexBasis; - copy.spacingAfter = self.spacingAfter; - copy.spacingBefore = self.spacingBefore; - copy.flexGrow = self.flexGrow; - copy.flexShrink = self.flexShrink; - - copy.ascender = self.ascender; - copy.descender = self.descender; - - copy.sizeRange = self.sizeRange; - copy.layoutPosition = self.layoutPosition; -} - - -#pragma mark - Defaults -- (void)setupDefaults -{ - _flexBasis = ASRelativeDimensionUnconstrained; - _spacingBefore = 0; - _spacingAfter = 0; - _flexGrow = NO; - _flexShrink = NO; - _alignSelf = ASStackLayoutAlignSelfAuto; - - _ascender = 0; - _descender = 0; - - _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _layoutPosition = CGPointZero; -} - -// Do this here instead of in Node/Spec subclasses so that custom specs can set default values -- (void)setValuesFromLayoutable:(id)layoutable -{ - if ([layoutable isKindOfClass:[ASTextNode class]]) { - ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); - } - if ([layoutable isKindOfClass:[ASDisplayNode class]]) { - ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; - self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.layoutPosition = displayNode.frame.origin; - } -} - - -@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm new file mode 100644 index 0000000000..ad6d224426 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASLayoutOptions.h" + +#import +#import +#import +#import "ASInternalHelpers.h" + +@interface ASLayoutOptions() +{ + ASDN::RecursiveMutex _propertyLock; +} +@end + +@implementation ASLayoutOptions + +@synthesize spacingBefore = _spacingBefore; +@synthesize spacingAfter = _spacingAfter; +@synthesize flexGrow = _flexGrow; +@synthesize flexShrink = _flexShrink; +@synthesize flexBasis = _flexBasis; +@synthesize alignSelf = _alignSelf; + +@synthesize ascender = _ascender; +@synthesize descender = _descender; + +@synthesize sizeRange = _sizeRange; +@synthesize layoutPosition = _layoutPosition; + +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } + }); + return gDefaultLayoutOptionsClass; +} + +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; + } + return self; +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} + +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ + ASDN::MutexLocker l(_propertyLock); + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.layoutPosition = self.layoutPosition; +} + + +#pragma mark - Defaults +- (void)setupDefaults +{ + self.flexBasis = ASRelativeDimensionUnconstrained; + self.spacingBefore = 0; + self.spacingAfter = 0; + self.flexGrow = NO; + self.flexShrink = NO; + self.alignSelf = ASStackLayoutAlignSelfAuto; + + self.ascender = 0; + self.descender = 0; + + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + self.layoutPosition = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + ASDN::MutexLocker l(_propertyLock); + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.layoutPosition = displayNode.frame.origin; + } +} + +- (CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingAfter; +} + +- (void)setSpacingAfter:(CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + _spacingAfter = spacingAfter; +} + +- (CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingBefore; +} + +- (void)setSpacingBefore:(CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + _spacingBefore = spacingBefore; +} + +- (BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + return _flexGrow; +} + +- (void)setFlexGrow:(BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + _flexGrow = flexGrow; +} + +- (BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + return _flexShrink; +} + +- (void)setFlexShrink:(BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + _flexShrink = flexShrink; +} + +- (ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + return _flexBasis; +} + +- (void)setFlexBasis:(ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + _flexBasis = flexBasis; +} + +- (ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + return _alignSelf; +} + +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + _alignSelf = alignSelf; +} + +- (CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + return _ascender; +} + +- (void)setAscender:(CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + _ascender = ascender; +} + +- (CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + return _descender; +} + +- (void)setDescender:(CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + _descender = descender; +} + +- (ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + return _sizeRange; +} + +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + _sizeRange = sizeRange; +} + +- (CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + return _layoutPosition; +} + +- (void)setLayoutPosition:(CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + _layoutPosition = layoutPosition; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 6c506b655c..0985014b80 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -62,12 +62,10 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { ASLayoutOptions *layoutOptions = [child layoutOptions]; - layoutOptions.isMutable = NO; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; - finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; From 32582fca580d6adeb6af5d3254a3baa09394cf89 Mon Sep 17 00:00:00 2001 From: Garrett Moon Date: Tue, 1 Sep 2015 13:19:26 -0700 Subject: [PATCH 109/145] 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 ab1a644091..7258fc3461 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 99167a93d4..e6551d7a26 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 110/145] 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 0809fbf515..e9aa51e544 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 0000000000..6d124b6e0a --- /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 0000000000..2a131bfecd --- /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 5d3a9d9bef..4756835551 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 54891897dd..33edd6ed7c 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 b65d144f69..e4ba08c8f4 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 0000000000..6c012e3c04 --- /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 0000000000..ba7ab3428a --- /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 0000000000..a80c038249 --- /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 0000000000..1e14aa0329 --- /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 0000000000..d98549fd35 --- /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 0000000000..85855277e9 --- /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 0000000000..1dea563b77 --- /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 0000000000..9d036ce48e --- /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 0000000000..9eb2ee9b31 --- /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 0000000000..35d842827b --- /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 0000000000..f4324b5d3e --- /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 0000000000..56435a80df --- /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 0000000000..d0e9200d88 --- /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 0000000000..b8297f5fe6 --- /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 0000000000..ae9488711c --- /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 111/145] 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 b9cf41c87a..0363abba2f 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 112/145] update documentation. --- AsyncDisplayKit/ASDisplayNode.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0b639f2a70..0b28939255 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 113/145] style fix --- AsyncDisplayKit/ASDisplayNode.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 0b28939255..a1d670c7bb 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 114/145] add locks to ASLayoutOptions --- AsyncDisplayKit.xcodeproj/project.pbxproj | 12 ++++++------ AsyncDisplayKit/Layout/ASLayoutOptions.mm | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 07f1563cf4..bc841c2034 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -231,8 +231,8 @@ 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; - 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; }; + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */; }; 9C5FA35D1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35E1B90C9A500A62714 /* ASLayoutOptionsPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */; }; @@ -587,7 +587,7 @@ 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = ""; }; 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = ""; }; 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = ""; }; - 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = ""; }; + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptions.mm; path = AsyncDisplayKit/Layout/ASLayoutOptions.mm; sourceTree = ""; }; 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptionsPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h; sourceTree = ""; }; 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutOptionsPrivate.mm; path = AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm; sourceTree = ""; }; 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = ""; }; @@ -1010,7 +1010,7 @@ 9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */, 9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */, 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */, - 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */, + 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.mm */, 9C5FA35B1B90C9A500A62714 /* ASLayoutOptionsPrivate.h */, 9C5FA35C1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm */, ); @@ -1477,7 +1477,7 @@ ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, - 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, @@ -1606,7 +1606,7 @@ B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */, B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, - 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */, + 9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */, diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm index ad6d224426..9e499bd172 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -117,8 +117,10 @@ static Class gDefaultLayoutOptionsClass = nil; ASDN::MutexLocker l(_propertyLock); if ([layoutable isKindOfClass:[ASTextNode class]]) { ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + if (textNode.attributedString.length > 0) { + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } } if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; From 7446578f6f748691154b5f73975f399ac7240364 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:04:06 -0700 Subject: [PATCH 115/145] 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 e9aa51e544..cadfc1ed66 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 05698813f4..13ee5fdf00 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 fd3cfce134..ea18aca990 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kBackgroundChildKey = @"kBackgroundChildKey"; + @interface ASBackgroundLayoutSpec () -{ - id _child; - id _background; -} @end @implementation ASBackgroundLayoutSpec @@ -30,12 +28,11 @@ } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - _child = child; - _background = background; + [self setChild:child]; + self.background = background; return self; } - + (instancetype)backgroundLayoutSpecWithChild:(id)child background:(id)background; { return [[self alloc] initWithChild:child background:background]; @@ -46,12 +43,12 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2]; - if (_background) { + if (self.background) { // Size background to exactly the same size. - ASLayout *backgroundLayout = [_background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; backgroundLayout.position = CGPointZero; [sublayouts addObject:backgroundLayout]; } @@ -63,14 +60,12 @@ - (void)setBackground:(id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _background = background; + [super setChild:background forIdentifier:kBackgroundChildKey]; } -- (void)setChild:(id)child +- (id)background { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; + return [super childForIdentifier:kBackgroundChildKey]; } @end diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h index 8b784a56c3..46587d996b 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.h @@ -42,9 +42,6 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) { /** The type of baseline alignment */ @property (nonatomic, assign) ASBaselineLayoutBaselineAlignment baselineAlignment; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - /** @param direction The direction of the stack view (horizontal or vertical) @param spacing The spacing between the children diff --git a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm index 670c69c991..45fa687f8c 100644 --- a/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm @@ -27,7 +27,6 @@ @implementation ASBaselineLayoutSpec { - std::vector> _children; ASDN::RecursiveMutex _propertyLock; } @@ -47,10 +46,7 @@ _justifyContent = justifyContent; _baselineAlignment = baselineAlignment; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } @@ -65,7 +61,12 @@ ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle }; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, stackStyle, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, stackStyle, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, stackStyle, constrainedSize); const auto baselinePositionedLayout = ASBaselinePositionedLayout::compute(positionedLayout, style, constrainedSize); @@ -82,16 +83,19 @@ sublayouts:sublayouts]; } -- (void)addChild:(id)child +- (void)setChildren:(NSArray *)children { - _children.push_back(child); + [super setChildren:children]; +#if DEBUG + for (id child in children) { + NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif } -- (void)addChildren:(NSArray *)children +- (void)setChild:(id)child forIdentifier:(NSString *)identifier { - for (id child in children) { - [self addChild:child]; - } + ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren"); } @end diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index 1830895320..58dc55f962 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -17,7 +17,6 @@ { ASCenterLayoutSpecCenteringOptions _centeringOptions; ASCenterLayoutSpecSizingOptions _sizingOptions; - id _child; } - (instancetype)initWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions @@ -30,7 +29,7 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _centeringOptions = centeringOptions; _sizingOptions = sizingOptions; - _child = child; + [self setChild:child]; return self; } @@ -41,12 +40,6 @@ return [[self alloc] initWithCenteringOptions:centeringOptions sizingOptions:sizingOptions child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -71,7 +64,7 @@ (_centeringOptions & ASCenterLayoutSpecCenteringX) != 0 ? 0 : constrainedSize.min.width, (_centeringOptions & ASCenterLayoutSpecCenteringY) != 0 ? 0 : constrainedSize.min.height, }; - ASLayout *sublayout = [_child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; + ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)]; // If we have an undetermined height or width, use the child size to define the layout // size diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 5ca96ab848..cc6ec9411a 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -19,7 +19,6 @@ @interface ASInsetLayoutSpec () { UIEdgeInsets _insets; - id _child; } @end @@ -50,7 +49,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) } ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); _insets = insets; - _child = child; + [self setChild:child]; return self; } @@ -59,12 +58,6 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) return [[self alloc] initWithInsets:insets child:child]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setInsets:(UIEdgeInsets)insets { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -95,7 +88,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner) MAX(0, constrainedSize.max.height - insetsY), } }; - ASLayout *sublayout = [_child measureWithSizeRange:insetConstrainedSize]; + ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize]; const CGSize computedSize = ASSizeRangeClamp(constrainedSize, { finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width), diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index bb200cbfe1..f599c84813 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -22,4 +22,13 @@ - (instancetype)init; +- (void)setChild:(id)child; +- (id)child; + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier; +- (id)childForIdentifier:(NSString *)identifier; + +- (void)setChildren:(NSArray *)children; +- (NSArray *)children; + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index ef09aa365e..58ecd114e9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -16,6 +16,13 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +static NSString * const kDefaultChildKey = @"kDefaultChildKey"; +static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; + +@interface ASLayoutSpec() +@property (nonatomic, strong) NSMutableDictionary *layoutChildren; +@end + @implementation ASLayoutSpec @synthesize spacingBefore = _spacingBefore; @@ -24,12 +31,14 @@ @synthesize flexShrink = _flexShrink; @synthesize flexBasis = _flexBasis; @synthesize alignSelf = _alignSelf; +@synthesize layoutChildren = _layoutChildren; - (instancetype)init { if (!(self = [super init])) { return nil; } + _layoutChildren = [NSMutableDictionary dictionary]; _flexBasis = ASRelativeDimensionUnconstrained; _isMutable = YES; return self; @@ -42,4 +51,45 @@ return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } +- (id)finalLayoutable +{ + return self; +} + +- (void)setChild:(id)child; +{ + [self setChild:child forIdentifier:kDefaultChildKey]; +} + +- (id)child +{ + return self.layoutChildren[kDefaultChildKey]; +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + self.layoutChildren[identifier] = [child finalLayoutable]; +} + +- (id)childForIdentifier:(NSString *)identifier +{ + return self.layoutChildren[identifier]; +} + +- (void)setChildren:(NSArray *)children +{ + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); + NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; + for (id child in children) { + [finalChildren addObject:[child finalLayoutable]]; + } + self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; +} + +- (NSArray *)children +{ + return self.layoutChildren[kDefaultChildrenKey]; +} + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 4ac01ee05f..4ff1ee030a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -12,6 +12,7 @@ #import @class ASLayout; +@class ASLayoutSpec; /** * The ASLayoutable protocol declares a method for measuring the layout of an object. A class must implement the method @@ -29,4 +30,16 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; +/** + @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before + being added to a ASLayoutSpec. + + For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement + layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method + a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. + + It is rare that a class will need to implement this method. + */ +- (id)finalLayoutable; + @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index f4f025573b..05c1c0c4ca 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -14,11 +14,9 @@ #import "ASBaseDefines.h" #import "ASLayout.h" +static NSString * const kOverlayChildKey = @"kOverlayChildKey"; + @implementation ASOverlayLayoutSpec -{ - id _overlay; - id _child; -} - (instancetype)initWithChild:(id)child overlay:(id)overlay { @@ -26,8 +24,8 @@ return nil; } ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - _overlay = overlay; - _child = child; + self.overlay = overlay; + [self setChild:child]; return self; } @@ -36,16 +34,14 @@ return [[self alloc] initWithChild:child overlay:overlay]; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setOverlay:(id)overlay { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _overlay = overlay; + [super setChild:overlay forIdentifier:kOverlayChildKey]; +} + +- (id)overlay +{ + return [super childForIdentifier:kOverlayChildKey]; } /** @@ -56,8 +52,8 @@ ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; - if (_overlay) { - ASLayout *overlayLayout = [_overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; + if (self.overlay) { + ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}]; overlayLayout.position = CGPointZero; [sublayouts addObject:overlayLayout]; } diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 5e5c6f0c66..64e9e2c118 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -22,7 +22,6 @@ @implementation ASRatioLayoutSpec { CGFloat _ratio; - id _child; } + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child @@ -38,16 +37,10 @@ ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); ASDisplayNodeAssert(ratio > 0, @"Ratio should be strictly positive, but received %f", ratio); _ratio = ratio; - _child = child; + [self setChild:child]; return self; } -- (void)setChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _child = child; -} - - (void)setRatio:(CGFloat)ratio { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -77,7 +70,7 @@ // If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through. const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize); - ASLayout *sublayout = [_child measureWithSizeRange:childRange]; + ASLayout *sublayout = [self.child measureWithSizeRange:childRange]; sublayout.position = CGPointZero; return [ASLayout layoutWithLayoutableObject:self size:sublayout.size sublayouts:@[sublayout]]; } diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h index 7825a9b8fc..b5a126c5cb 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.h @@ -55,7 +55,4 @@ */ + (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children; -- (void)addChild:(id)child; -- (void)addChildren:(NSArray *)children; - @end diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 3149979dd6..07243f30cc 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -23,9 +23,6 @@ #import "ASThread.h" @implementation ASStackLayoutSpec -{ - std::vector> _children; -} - (instancetype)init { @@ -47,26 +44,10 @@ _spacing = spacing; _justifyContent = justifyContent; - _children = std::vector>(); - for (id child in children) { - _children.push_back(child); - } + [self setChildren:children]; return self; } -- (void)addChild:(id)child -{ - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - _children.push_back(child); -} - -- (void)addChildren:(NSArray *)children -{ - for (id child in children) { - [self addChild:child]; - } -} - - (void)setDirection:(ASStackLayoutDirection)direction { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); @@ -91,10 +72,32 @@ _spacing = spacing; } +- (void)setChildren:(NSArray *)children +{ + [super setChildren:children]; + +#if DEBUG + for (id child in children) { + ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable"); + } +#endif +} + +- (void)setChild:(id)child forIdentifier:(NSString *)identifier +{ + ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren"); +} + - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; - const auto unpositionedLayout = ASStackUnpositionedLayout::compute(_children, style, constrainedSize); + std::vector> stackChildren = std::vector>(); + for (id child in self.children) { + NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable"); + stackChildren.push_back(child); + } + + const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, positionedLayout.crossSize); NSArray *sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()]; diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 38ed2f06d6..8bd3f3d9f2 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -11,43 +11,6 @@ #import #import -/** - * An ASStaticLayoutSpecChild object wraps an ASLayoutable object and provides position and size information, - * to be used as a child of an ASStaticLayoutSpec. - */ -@interface ASStaticLayoutSpecChild : NSObject - -@property (nonatomic, readonly) CGPoint position; -@property (nonatomic, readonly) id layoutableObject; - -/** - If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. - */ -@property (nonatomic, readonly) ASRelativeSizeRange size; - -/** - * Initializer. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - * - * @param size The size range that this child's size is trstricted according to. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject size:(ASRelativeSizeRange)size; - -/** - * Convenience initializer with default size is Unconstrained in both dimensions, which sets the child's min size to zero - * and max size to the maximum available space it can consume without overflowing the spec's bounds. - * - * @param position The position of this child within its parent spec. - * - * @param layoutableObject The backing ASLayoutable object of this child. - */ -+ (instancetype)staticLayoutChildWithPosition:(CGPoint)position layoutableObject:(id)layoutableObject; - -@end - /** * A layout spec that positions children at fixed positions. * @@ -56,10 +19,8 @@ @interface ASStaticLayoutSpec : ASLayoutSpec /** - @param children Children to be positioned at fixed positions, each is of type ASStaticLayoutSpecChild. + @param children Children to be positioned at fixed positions, each conforms to ASStaticLayoutable */ + (instancetype)staticLayoutSpecWithChildren:(NSArray *)children; -- (void)addChild:(ASStaticLayoutSpecChild *)child; - @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index eca2637acd..585bf94660 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 0000000000..5618fa6e9b --- /dev/null +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import +#import + +@protocol ASStaticLayoutable + +/** + If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec. + */ +@property (nonatomic, assign) ASRelativeSizeRange sizeRange; + +/** The position of this object within its parent spec. */ +@property (nonatomic, assign) CGPoint position; + +@end From 61b72d2c46dbf6a9e1e4ede38682d3868f89b766 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 25 Aug 2015 14:37:34 -0700 Subject: [PATCH 116/145] 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 0afd654bf8..2be42824b1 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 117/145] 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; From cbaf1789500704c56555d883a8bbe31cbf190ea3 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 14:30:50 -0700 Subject: [PATCH 118/145] 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 441430497b..ce6773d44b 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 4b5419cc40..54a2d94b1e 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 0bdee1ad09..091f078ae9 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -16,6 +16,9 @@ @interface ASLayoutOptions : NSObject ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass; ++ (Class)defaultLayoutOptionsClass; + - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; @@ -42,7 +45,7 @@ #pragma mark - ASStaticLayoutable @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint position; +@property (nonatomic, readwrite) CGPoint layoutPosition; - (void)setupDefaults; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index 6207ef0d07..df3fc0fb61 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -15,6 +15,18 @@ @implementation ASLayoutOptions +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + return gDefaultLayoutOptionsClass; +} + + - (instancetype)initWithLayoutable:(id)layoutable; { self = [super init]; @@ -79,7 +91,7 @@ copy.descender = self.descender; copy.sizeRange = self.sizeRange; - copy.position = self.position; + copy.layoutPosition = self.layoutPosition; return copy; } @@ -98,7 +110,7 @@ _descender = 0; _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _position = CGPointZero; + _layoutPosition = CGPointZero; } // Do this here instead of in Node/Spec subclasses so that custom specs can set default values @@ -112,7 +124,7 @@ if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.position = displayNode.frame.origin; + self.layoutPosition = displayNode.frame.origin; } } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h new file mode 100644 index 0000000000..f7b7f9b9fc --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -0,0 +1,30 @@ +// +// ASDisplayNode+Layoutable.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import +#import + + +@interface ASDisplayNode() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASDisplayNode(ASLayoutOptions) +@end + +@interface ASLayoutSpec() +{ + ASLayoutOptions *_layoutOptions; +} +@end + +@interface ASLayoutSpec(ASLayoutOptions) +@end + diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm new file mode 100644 index 0000000000..b0332a8007 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -0,0 +1,129 @@ +// +// ASDisplayNode+Layoutable.m +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// + +#import "ASLayoutOptionsPrivate.h" +#import + + +#define ASLayoutOptionsForwarding \ +- (ASLayoutOptions *)layoutOptions\ +{\ +if (_layoutOptions == nil) {\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ +return _layoutOptions;\ +}\ +\ +- (CGFloat)spacingBefore\ +{\ +return self.layoutOptions.spacingBefore;\ +}\ +\ +- (void)setSpacingBefore:(CGFloat)spacingBefore\ +{\ +self.layoutOptions.spacingBefore = spacingBefore;\ +}\ +\ +- (CGFloat)spacingAfter\ +{\ +return self.layoutOptions.spacingAfter;\ +}\ +\ +- (void)setSpacingAfter:(CGFloat)spacingAfter\ +{\ +self.layoutOptions.spacingAfter = spacingAfter;\ +}\ +\ +- (BOOL)flexGrow\ +{\ +return self.layoutOptions.flexGrow;\ +}\ +\ +- (void)setFlexGrow:(BOOL)flexGrow\ +{\ +self.layoutOptions.flexGrow = flexGrow;\ +}\ +\ +- (BOOL)flexShrink\ +{\ +return self.layoutOptions.flexShrink;\ +}\ +\ +- (void)setFlexShrink:(BOOL)flexShrink\ +{\ +self.layoutOptions.flexShrink = flexShrink;\ +}\ +\ +- (ASRelativeDimension)flexBasis\ +{\ +return self.layoutOptions.flexBasis;\ +}\ +\ +- (void)setFlexBasis:(ASRelativeDimension)flexBasis\ +{\ +self.layoutOptions.flexBasis = flexBasis;\ +}\ +\ +- (ASStackLayoutAlignSelf)alignSelf\ +{\ +return self.layoutOptions.alignSelf;\ +}\ +\ +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\ +{\ + self.layoutOptions.alignSelf = alignSelf;\ +}\ +\ +- (CGFloat)ascender\ +{\ + return self.layoutOptions.ascender;\ +}\ +\ +- (void)setAscender:(CGFloat)ascender\ +{\ + self.layoutOptions.ascender = ascender;\ +}\ +\ +- (CGFloat)descender\ +{\ + return self.layoutOptions.descender;\ +}\ +\ +- (void)setDescender:(CGFloat)descender\ +{\ + self.layoutOptions.descender = descender;\ +}\ +\ +- (ASRelativeSizeRange)sizeRange\ +{\ + return self.layoutOptions.sizeRange;\ +}\ +\ +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\ +{\ + self.layoutOptions.sizeRange = sizeRange;\ +}\ +\ +- (CGPoint)layoutPosition\ +{\ + return self.layoutOptions.layoutPosition;\ +}\ +\ +- (void)setLayoutPosition:(CGPoint)position\ +{\ + self.layoutOptions.layoutPosition = position;\ +}\ + + +@implementation ASDisplayNode(ASLayoutOptions) +ASLayoutOptionsForwarding +@end + +@implementation ASLayoutSpec(ASLayoutOptions) +ASLayoutOptionsForwarding +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index dc4f100bb5..494d5d1198 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,8 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; +//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; +//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; +//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index f277a01bc4..994ad8428e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -15,6 +15,8 @@ #import "ASInternalHelpers.h" #import "ASLayout.h" +#import "ASLayoutOptions.h" +#import "ASLayoutOptionsPrivate.h" #import @@ -27,6 +29,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; @implementation ASLayoutSpec +@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition, layoutOptions; @synthesize layoutChildren = _layoutChildren; - (instancetype)init @@ -71,16 +74,20 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + if ([child respondsToSelector:@selector(finalLayoutable)]) { + id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; + } + } else { + [finalChildren addObject:child]; } - [finalChildren addObject:finalLayoutable]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 8ba86fdc1b..2ea76f3983 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -9,8 +9,10 @@ */ #import +#import #import -#import + +#import @class ASLayout; @class ASLayoutSpec; @@ -20,7 +22,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -31,16 +33,17 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -/** - @abstract Give this object a last chance to add itself to a container ASLayoutable (most likely an ASLayoutSpec) before - being added to a ASLayoutSpec. - - For example, consider a node whose superclass is laid out via calculateLayoutThatFits:. The subclass cannot implement - layoutSpecThatFits: since its ASLayout is already being created by calculateLayoutThatFits:. By implementing this method - a subclass can wrap itself in an ASLayoutSpec right before it is added to a layout spec. - - It is rare that a class will need to implement this method. - */ -- (id)finalLayoutable; +@property (nonatomic, readwrite) CGFloat spacingBefore; +@property (nonatomic, readwrite) CGFloat spacingAfter; +@property (nonatomic, readwrite) BOOL flexGrow; +@property (nonatomic, readwrite) BOOL flexShrink; +@property (nonatomic, readwrite) ASRelativeDimension flexBasis; +@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; + +@property (nonatomic, readwrite) CGFloat ascender; +@property (nonatomic, readwrite) CGFloat descender; + +@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; +@property (nonatomic, readwrite) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index b458207b8e..e9f47c2ecd 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 8e7d74acf3..f2e565a7a9 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -18,6 +18,6 @@ @property (nonatomic, assign) ASRelativeSizeRange sizeRange; /** The position of this object within its parent spec. */ -@property (nonatomic, assign) CGPoint position; +@property (nonatomic, assign) CGPoint layoutPosition; @end diff --git a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm index bf0239c952..bf01ff179e 100644 --- a/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASBaselinePositionedLayout.mm @@ -12,17 +12,19 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style, const ASLayout *layout) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject]; + + __weak id child = layout.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentNone: return 0; case ASBaselineLayoutBaselineAlignmentFirst: - return layoutOptions.ascender; + return child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: - return layout.size.height + layoutOptions.descender; + return layout.size.height + child.layoutOptions.descender; } } @@ -33,10 +35,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style, const CGFloat maxBaseline) { if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; + __weak id child = l.layoutableObject; switch (style.baselineAlignment) { case ASBaselineLayoutBaselineAlignmentFirst: - return maxAscender - layoutOptions.ascender; + return maxAscender - child.layoutOptions.ascender; case ASBaselineLayoutBaselineAlignmentLast: return maxBaseline - baselineForItem(style, l); case ASBaselineLayoutBaselineAlignmentNone: @@ -90,11 +92,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec. */ const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){ - ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject]; - ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject]; - return layoutOptionsA.ascender < layoutOptionsB.ascender; + return a.layoutableObject.layoutOptions.ascender < b.layoutableObject.layoutOptions.ascender; }); - const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender; + const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.layoutOptions.ascender; /* Step 3: Take each child and update its layout position based on the baseline offset. @@ -107,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi CGPoint p = CGPointZero; BOOL first = YES; auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject]; - p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0); + __weak id child = l.layoutableObject; + p = p + directionPoint(stackStyle.direction, child.layoutOptions.spacingBefore, 0); if (first) { // if this is the first item use the previously computed start point p = l.position; @@ -125,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi // node from baselines and not bounding boxes. CGFloat spacingAfterBaseline = 0; if (stackStyle.direction == ASStackLayoutDirectionVertical) { - spacingAfterBaseline = layoutOptions.descender; + spacingAfterBaseline = child.layoutOptions.descender; } - p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0); + p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.layoutOptions.spacingAfter + spacingAfterBaseline, 0); return l; }); @@ -157,7 +157,7 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){ return a.position.y + a.size.height < b.position.y + b.size.height; }); - const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender; + const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.layoutOptions.descender; return {stackedChildren, crossSize, maxAscender, minDescender}; } diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 5a8883ed02..d727bc5bab 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -17,6 +17,7 @@ #import "ASDisplayNode.h" #import "ASSentinel.h" #import "ASThread.h" +#import "ASLayoutOptions.h" BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector); void ASDisplayNodePerformBlockOnMainThread(void (^block)()); @@ -71,7 +72,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { NSMutableSet *_pendingDisplayNodes; _ASPendingState *_pendingViewState; - + struct { // public properties unsigned synchronous:1; @@ -153,6 +154,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; +- (id)finalLayoutable; + @end @interface UIView (ASDisplayNodeInternal) diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h new file mode 100644 index 0000000000..360c91570b --- /dev/null +++ b/AsyncDisplayKit/Private/ASLayoutablePrivate.h @@ -0,0 +1,16 @@ +// +// ASLayoutablePrivate.h +// AsyncDisplayKit +// +// Created by Ricky Cancro on 8/28/15. +// Copyright (c) 2015 Facebook. All rights reserved. +// +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 31ff00b595..95b445043c 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -14,13 +14,13 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" #import "ASLayoutable.h" +#import "ASLayoutOptions.h" static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, const CGFloat crossSize) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - switch (alignment(layoutOptions.alignSelf, style.alignItems)) { + switch (alignment(l.child.layoutOptions.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsEnd: return crossSize - crossDimension(style.direction, l.layout.size); case ASStackLayoutAlignItemsCenter: @@ -49,15 +49,14 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style CGPoint p = directionPoint(style.direction, offset, 0); BOOL first = YES; auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{ - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0); + p = p + directionPoint(style.direction, l.child.layoutOptions.spacingBefore, 0); if (!first) { p = p + directionPoint(style.direction, style.spacing, 0); } first = NO; l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize)); - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0); + p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.layoutOptions.spacingAfter, 0); return l.layout; }); return {stackedChildren, crossSize}; diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index da4000cd5e..7514507a01 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -14,6 +14,7 @@ #import "ASLayoutSpecUtilities.h" #import "ASStackLayoutSpecUtilities.h" +#import "ASLayoutOptions.h" /** Sizes the child given the parameters specified, and returns the computed layout. @@ -25,8 +26,7 @@ static ASLayout *crossChildLayout(const id child, const CGFloat crossMin, const CGFloat crossMax) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(child.layoutOptions.alignSelf, style.alignItems); // stretched children will have a cross dimension of at least crossMin const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); @@ -76,8 +76,7 @@ static void stretchChildrenAlongCrossDimension(std::vectorlayout.size); for (auto &l : layouts) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child]; - const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems); + const ASStackLayoutAlignItems alignItems = alignment(l.child.layoutOptions.alignSelf, style.alignItems); const CGFloat cross = crossDimension(style.direction, l.layout.size); const CGFloat stack = stackDimension(style.direction, l.layout.size); @@ -113,8 +112,7 @@ static CGFloat computeStackDimensionSum(const std::vector isFlexibleInViolatio if (fabs(violation) < kViolationEpsilon) { return [](const ASStackUnpositionedItem &l) { return NO; }; } else if (violation > 0) { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexGrow; }; } else { - return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; }; + return [](const ASStackUnpositionedItem &l) { return l.child.layoutOptions.flexShrink; }; } } ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id child) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - return layoutOptions.flexGrow && layoutOptions.flexShrink; + return child.layoutOptions.flexGrow && child.layoutOptions.flexShrink; } /** @@ -297,9 +294,8 @@ static std::vector layoutChildrenAlongUnconstrainedStac const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); return AS::map(children, [&](id child) -> ASStackUnpositionedItem { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis); - const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size)); + const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.layoutOptions.flexBasis); + const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.layoutOptions.flexBasis, stackDimension(style.direction, size)); if (useOptimizedFlexing && isFlexibleInBothDirections(child)) { return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] }; diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 6ea803ba93..45e167afd7 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -13,6 +13,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASCenterLayoutSpec.h" #import "ASStackLayoutSpec.h" +#import "ASLayoutOptions.h" static const ASSizeRange kSize = {{100, 120}, {320, 160}}; @@ -94,7 +95,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); foregroundNode.staticSize = {10, 10}; - [ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES; + foregroundNode.layoutOptions.flexGrow = YES; ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 2e9c2e7802..fbd893940b 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -15,6 +15,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASRatioLayoutSpec.h" #import "ASInsetLayoutSpec.h" +#import "ASLayoutOptions.h" @interface ASStackLayoutSpecSnapshotTests : ASLayoutSpecSnapshotTestCase @end @@ -41,8 +42,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ]; for (ASStaticSizeDisplayNode *subnode in subnodes) { subnode.staticSize = subnodeSize; - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex; - [ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex; + subnode.layoutOptions.flexGrow = flex; + subnode.layoutOptions.flexShrink = flex; } return subnodes; } @@ -114,7 +115,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); - [ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES; + ((ASDisplayNode *)subnodes[1]).layoutOptions.flexShrink = YES; // Width is 75px--that's less than the sum of the widths of the children, which is 100px. static ASSizeRange kSize = {{75, 0}, {75, 150}}; @@ -204,25 +205,23 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]]; - layoutOptions1.spacingBefore = 10; - layoutOptions2.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; // Reset above spacing values - layoutOptions1.spacingBefore = 0; - layoutOptions2.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 0; - layoutOptions1.spacingAfter = 10; - layoutOptions2.spacingAfter = 20; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 10; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 20; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; // Reset above spacing values - layoutOptions1.spacingAfter = 0; - layoutOptions2.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = 0; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingAfter = 0; style.spacing = 10; - layoutOptions1.spacingBefore = -10; - layoutOptions2.spacingAfter = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = -10; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingAfter = -10; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; } @@ -238,9 +237,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; // width 0-300px; height 300px static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; @@ -256,10 +255,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) subnode2.staticSize = {50, 50}; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; - ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1]; - layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1); - layoutOptions1.flexGrow = YES; - layoutOptions1.flexShrink = YES; + child1.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(1); + child1.layoutOptions.flexGrow = YES; + child1.layoutOptions.flexShrink = YES; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; [self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil]; @@ -274,11 +272,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); subnode1.staticSize = {100, 100}; - [ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES; + subnode1.layoutOptions.flexShrink = YES; ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES; + subnode2.layoutOptions.flexShrink = YES; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; @@ -294,7 +292,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); subnode2.staticSize = {50, 50}; - [ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter; + subnode2.layoutOptions.alignSelf = ASStackLayoutAlignSelfCenter; NSArray *subnodes = @[subnode1, subnode2]; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; @@ -314,9 +312,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -335,9 +333,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -356,9 +354,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; @@ -377,9 +375,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; // all children should be 200px wide @@ -399,9 +397,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.spacingBefore = 0; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.spacingBefore = 20; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.spacingBefore = 30; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; // all children should be 150px wide @@ -422,9 +420,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode]; - layoutOptions.flexGrow = YES; - layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); + subnode.layoutOptions.flexGrow = YES; + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10); } // width 300px; height 0-150px. @@ -443,12 +440,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES; + subnode.layoutOptions.flexGrow = YES; } // This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // The result should be that the red box is twice as wide as the blue and gree boxes after flexing. - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5); + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexBasis = ASRelativeDimensionMakeWithPercent(0.5); static ASSizeRange kSize = {{200, 0}, {200, INFINITY}}; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; @@ -464,7 +461,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; for (ASStaticSizeDisplayNode *subnode in subnodes) { - [ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20); + subnode.layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(20); } static ASSizeRange kSize = {{300, 0}, {300, 150}}; @@ -482,9 +479,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000}; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; - ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2]; - layoutOptions2.flexGrow = YES; - layoutOptions2.flexShrink = YES; + child2.layoutOptions.flexGrow = YES; + child2.layoutOptions.flexShrink = YES; // If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall. // Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset. @@ -509,13 +505,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) NSArray *subnodes = defaultSubnodes(); ((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[0]).layoutOptions.flexShrink = YES; ((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO; + ((ASStaticSizeDisplayNode *)subnodes[1]).layoutOptions.flexShrink = NO; ((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50}; - [ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES; + ((ASStaticSizeDisplayNode *)subnodes[2]).layoutOptions.flexShrink = YES; // A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, // causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px. diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 2be42824b1..d1f49351a2 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 119/145] 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 ce6773d44b..b62dc5f963 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 091f078ae9..3df0f7c673 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index df3fc0fb61..a383d8497e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -1,10 +1,12 @@ -// -// ASLayoutOptions.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/27/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptions.h" @@ -23,6 +25,11 @@ static Class gDefaultLayoutOptionsClass = nil; + (Class)defaultLayoutOptionsClass { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + }); return gDefaultLayoutOptionsClass; } diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h index f7b7f9b9fc..b00e09580a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.h @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import #import @@ -13,6 +15,7 @@ @interface ASDisplayNode() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end @@ -22,6 +25,7 @@ @interface ASLayoutSpec() { ASLayoutOptions *_layoutOptions; + dispatch_once_t _layoutOptionsInitializeToken; } @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index b0332a8007..431565bde1 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -1,10 +1,12 @@ -// -// ASDisplayNode+Layoutable.m -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ #import "ASLayoutOptionsPrivate.h" #import @@ -13,9 +15,9 @@ #define ASLayoutOptionsForwarding \ - (ASLayoutOptions *)layoutOptions\ {\ -if (_layoutOptions == nil) {\ +dispatch_once(&_layoutOptionsInitializeToken, ^{\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ -}\ +});\ return _layoutOptions;\ }\ \ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 494d5d1198..f132d5d7f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -30,8 +30,4 @@ - (id)childForIdentifier:(NSString *)identifier; - (NSArray *)children; -//+ (ASLayoutOptions *)layoutOptionsForChild:(id)child; -//+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child; -//+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 994ad8428e..39487fe1ec 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -74,20 +74,17 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - if ([child respondsToSelector:@selector(finalLayoutable)]) { - id finalLayoutable = [child performSelector:@selector(finalLayoutable)]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + [finalChildren addObject:finalLayoutable]; } else { [finalChildren addObject:child]; } - } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index 2ea76f3983..38644c0b60 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -11,6 +11,9 @@ #import #import #import +#import +#import +#import #import @@ -22,7 +25,7 @@ * so that instances of that class can be used to build layout trees. The protocol also provides information * about how an object should be laid out within an ASStackLayoutSpec. */ -@protocol ASLayoutable +@protocol ASLayoutable /** * @abstract Calculate a layout based on given size range. @@ -33,17 +36,4 @@ */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize; -@property (nonatomic, readwrite) CGFloat spacingBefore; -@property (nonatomic, readwrite) CGFloat spacingAfter; -@property (nonatomic, readwrite) BOOL flexGrow; -@property (nonatomic, readwrite) BOOL flexShrink; -@property (nonatomic, readwrite) ASRelativeDimension flexBasis; -@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf; - -@property (nonatomic, readwrite) CGFloat ascender; -@property (nonatomic, readwrite) CGFloat descender; - -@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; -@property (nonatomic, readwrite) CGPoint layoutPosition; - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h new file mode 100644 index 0000000000..5091a30954 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import + +@class ASLayoutSpec; +@class ASLayoutOptions; + +@protocol ASLayoutablePrivate +- (ASLayoutSpec *)finalLayoutable; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +@end diff --git a/AsyncDisplayKit/Private/ASLayoutablePrivate.h b/AsyncDisplayKit/Private/ASLayoutablePrivate.h deleted file mode 100644 index 360c91570b..0000000000 --- a/AsyncDisplayKit/Private/ASLayoutablePrivate.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// ASLayoutablePrivate.h -// AsyncDisplayKit -// -// Created by Ricky Cancro on 8/28/15. -// Copyright (c) 2015 Facebook. All rights reserved. -// -#import - -@class ASLayoutSpec; -@class ASLayoutOptions; - -@protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; -@end From 36d00273fb8afade37a48b02f6b7da50bc081730 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:49:40 -0700 Subject: [PATCH 120/145] wasn't copying layout options to final layoutable. --- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 32 ++++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 39487fe1ec..a1ba8ed80a 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -59,12 +59,25 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; [self setChild:child forIdentifier:kDefaultChildKey]; } +- (id)layoutableToAddFromLayoutable:(id)child +{ + ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; + id finalLayoutable = [child finalLayoutable]; + layoutOptions.isMutable = NO; + + if (finalLayoutable != child) { + ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; + finalLayoutOptions.isMutable = NO; + [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + return finalLayoutable; + } + return child; +} + - (void)setChild:(id)child forIdentifier:(NSString *)identifier { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - layoutOptions.isMutable = NO; - self.layoutChildren[identifier] = child; + self.layoutChildren[identifier] = [self layoutableToAddFromLayoutable:child];; } - (void)setChildren:(NSArray *)children @@ -73,18 +86,7 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count]; for (id child in children) { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; - layoutOptions.isMutable = NO; - - if (finalLayoutable != child) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; - [finalChildren addObject:finalLayoutable]; - } else { - [finalChildren addObject:child]; - } + [finalChildren addObject:[self layoutableToAddFromLayoutable:child]]; } self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren]; From 84cd80c41d43fbaaaef76032d2fefc1d790f93cb Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 16:51:15 -0700 Subject: [PATCH 121/145] bug in setting the ASLayoutOptions default class. --- AsyncDisplayKit/Layout/ASLayoutOptions.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index a383d8497e..e0ebf622f5 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -27,8 +27,10 @@ static Class gDefaultLayoutOptionsClass = nil; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } }); return gDefaultLayoutOptionsClass; } From 5786bc1b5bc363838ba988507df4945923e657d1 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:11:19 -0700 Subject: [PATCH 122/145] 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 54a2d94b1e..4dcf41e26e 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 ecc2e48473..cab51ea8b3 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASBackgroundLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id background; /** diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index dc50b265e7..37ba24e7e7 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -39,7 +39,6 @@ typedef NS_OPTIONS(NSUInteger, ASCenterLayoutSpecSizingOptions) { @property (nonatomic, assign) ASCenterLayoutSpecCenteringOptions centeringOptions; @property (nonatomic, assign) ASCenterLayoutSpecSizingOptions sizingOptions; -@property (nonatomic, strong) id child; /** * Initializer. diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h index 3b140dfcc7..ab0a6f106f 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h @@ -29,7 +29,6 @@ */ @interface ASInsetLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, assign) UIEdgeInsets insets; /** diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index 431565bde1..f04c010936 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -16,11 +16,18 @@ - (ASLayoutOptions *)layoutOptions\ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ +if (_layoutOptions == nil) {\ _layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +}\ });\ return _layoutOptions;\ }\ \ +- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ +{\ + _layoutOptions = layoutOptions;\ +}\ +\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index a1ba8ed80a..904c30cc69 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -49,9 +49,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return [ASLayout layoutWithLayoutableObject:self size:constrainedSize.min]; } -- (id)finalLayoutable +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; { - return self; + return nil; } - (void)setChild:(id)child; @@ -61,14 +61,14 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { - ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child]; - id finalLayoutable = [child finalLayoutable]; + ASLayoutOptions *layoutOptions = [child layoutOptions]; layoutOptions.isMutable = NO; - if (finalLayoutable != child) { + id finalLayoutable = [child finalLayoutableWithParent:self]; + if (finalLayoutable) { ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; finalLayoutOptions.isMutable = NO; - [ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable]; + finalLayoutable.layoutOptions = finalLayoutOptions; return finalLayoutable; } return child; @@ -107,35 +107,5 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; return self.layoutChildren[kDefaultChildrenKey]; } -static Class gLayoutOptionsClass = [ASLayoutOptions class]; -+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass -{ - gLayoutOptionsClass = layoutOptionsClass; -} - -+ (ASLayoutOptions *)optionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];; - [layoutOptions setValuesFromLayoutable:child]; - layoutOptions.isMutable = NO; - return layoutOptions; -} - -+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id)child -{ - objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -+ (ASLayoutOptions *)layoutOptionsForChild:(id)child -{ - ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:)); - if (layoutOptions == nil) { - layoutOptions = [self optionsForChild:child]; - [self associateLayoutOptions:layoutOptions withChild:child]; - } - return objc_getAssociatedObject(child, @selector(setChild:)); -} - - @end diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 5091a30954..c7ab3b7ad3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -14,6 +14,6 @@ @class ASLayoutOptions; @protocol ASLayoutablePrivate -- (ASLayoutSpec *)finalLayoutable; -@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; +- (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; +@property (nonatomic, strong) ASLayoutOptions *layoutOptions; @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h index 35a9577dde..05e53d92e8 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h @@ -15,7 +15,6 @@ */ @interface ASOverlayLayoutSpec : ASLayoutSpec -@property (nonatomic, strong) id child; @property (nonatomic, strong) id overlay; + (instancetype)overlayLayoutSpecWithChild:(id)child overlay:(id)overlay; diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 05c1c0c4ca..b71375f5be 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -49,7 +49,7 @@ static NSString * const kOverlayChildKey = @"kOverlayChildKey"; */ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize { - ASLayout *contentsLayout = [_child measureWithSizeRange:constrainedSize]; + ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; if (self.overlay) { diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h index fd7f6d657b..2affa56a75 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h @@ -31,8 +31,6 @@ **/ @interface ASRatioLayoutSpec : ASLayoutSpec - -@property (nonatomic, strong) id child; @property (nonatomic, assign) CGFloat ratio; + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index d727bc5bab..cff5d76461 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -154,8 +154,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides) { @property (nonatomic, assign) CGFloat contentsScaleForDisplay; -- (id)finalLayoutable; - @end @interface UIView (ASDisplayNodeInternal) From 2a8b20b102f586652880a13a7870a14883275c5c Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:14:09 -0700 Subject: [PATCH 123/145] didn't mean to commit this --- Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 119b17c410..423c2d2851 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,4 +10,4 @@ SPEC CHECKSUMS: FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2 -COCOAPODS: 0.35.0 +COCOAPODS: 0.37.2 From fca43a651f57f51dd0499ba03a75b1ffc9a6195a Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Fri, 28 Aug 2015 20:41:40 -0700 Subject: [PATCH 124/145] Make layoutOptions readonly --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 9 +++- AsyncDisplayKit/Layout/ASLayoutOptions.m | 44 +++++++++++-------- .../Layout/ASLayoutOptionsPrivate.mm | 7 +-- AsyncDisplayKit/Layout/ASLayoutSpec.mm | 5 +-- AsyncDisplayKit/Layout/ASLayoutablePrivate.h | 2 +- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 3df0f7c673..937f6c7640 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -24,6 +24,14 @@ - (instancetype)initWithLayoutable:(id)layoutable; - (void)setValuesFromLayoutable:(id)layoutable; +#pragma mark - Subclasses should implement these! ++ (NSSet *)keyPathsForValuesAffectingChangeMonitor; +- (void)setupDefaults; +- (instancetype)copyWithZone:(NSZone *)zone; +- (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; + +#pragma mark - Mutability checks + @property (nonatomic, assign) BOOL isMutable; #if DEBUG @@ -49,6 +57,5 @@ @property (nonatomic, readwrite) ASRelativeSizeRange sizeRange; @property (nonatomic, readwrite) CGPoint layoutPosition; -- (void)setupDefaults; @end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m index e0ebf622f5..9d8d9c6a0e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.m @@ -13,7 +13,6 @@ #import #import #import "ASInternalHelpers.h" -#import @implementation ASLayoutOptions @@ -35,6 +34,10 @@ static Class gDefaultLayoutOptionsClass = nil; return gDefaultLayoutOptionsClass; } +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} - (instancetype)initWithLayoutable:(id)layoutable; { @@ -42,30 +45,31 @@ static Class gDefaultLayoutOptionsClass = nil; if (self) { [self setupDefaults]; [self setValuesFromLayoutable:layoutable]; + _isMutable = YES; #if DEBUG - [self addObserver:self forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; + [self addObserver:self + forKeyPath:@"changeMonitor" + options:NSKeyValueObservingOptionNew + context:nil]; #endif } return self; } +- (void)dealloc +{ +#if DEBUG + [self removeObserver:self forKeyPath:@"changeMonitor"]; +#endif +} + #if DEBUG + (NSSet *)keyPathsForValuesAffectingChangeMonitor { NSMutableSet *keys = [NSMutableSet set]; - unsigned int count; - - objc_property_t *properties = class_copyPropertyList([self class], &count); - for (size_t i = 0; i < count; ++i) { - NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding]; - - if ([property isEqualToString: @"observableSelf"] == NO) { - [keys addObject: property]; - } - } - free(properties); + [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; + [keys addObjectsFromArray:@[@"ascender", @"descender"]]; + [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; return keys; } @@ -89,7 +93,12 @@ static Class gDefaultLayoutOptionsClass = nil; - (id)copyWithZone:(NSZone *)zone { ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ copy.flexBasis = self.flexBasis; copy.spacingAfter = self.spacingAfter; copy.spacingBefore = self.spacingBefore; @@ -98,13 +107,12 @@ static Class gDefaultLayoutOptionsClass = nil; copy.ascender = self.ascender; copy.descender = self.descender; - + copy.sizeRange = self.sizeRange; copy.layoutPosition = self.layoutPosition; - - return copy; } + #pragma mark - Defaults - (void)setupDefaults { diff --git a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm index f04c010936..39d129ba11 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptionsPrivate.mm @@ -17,17 +17,12 @@ {\ dispatch_once(&_layoutOptionsInitializeToken, ^{\ if (_layoutOptions == nil) {\ -_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] init];\ +_layoutOptions = [[[ASLayoutOptions defaultLayoutOptionsClass] alloc] initWithLayoutable:self];\ }\ });\ return _layoutOptions;\ }\ \ -- (void)setLayoutOptions:(ASLayoutOptions *)layoutOptions\ -{\ - _layoutOptions = layoutOptions;\ -}\ -\ - (CGFloat)spacingBefore\ {\ return self.layoutOptions.spacingBefore;\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 904c30cc69..6c506b655c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -66,9 +66,8 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { - ASLayoutOptions *finalLayoutOptions = [layoutOptions copy]; - finalLayoutOptions.isMutable = NO; - finalLayoutable.layoutOptions = finalLayoutOptions; + [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; + finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index c7ab3b7ad3..2d317fc7ca 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -15,5 +15,5 @@ @protocol ASLayoutablePrivate - (ASLayoutSpec *)finalLayoutableWithParent:(ASLayoutSpec *)parentSpec; -@property (nonatomic, strong) ASLayoutOptions *layoutOptions; +@property (nonatomic, strong, readonly) ASLayoutOptions *layoutOptions; @end From b3369ca388491341fe5afd0a04c6e220b887ad78 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Mon, 31 Aug 2015 14:19:27 -0700 Subject: [PATCH 125/145] making ASLayoutOptions threadsafe --- AsyncDisplayKit/Layout/ASLayoutOptions.h | 10 - AsyncDisplayKit/Layout/ASLayoutOptions.m | 149 ------------- AsyncDisplayKit/Layout/ASLayoutOptions.mm | 250 ++++++++++++++++++++++ AsyncDisplayKit/Layout/ASLayoutSpec.mm | 2 - 4 files changed, 250 insertions(+), 161 deletions(-) delete mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.m create mode 100644 AsyncDisplayKit/Layout/ASLayoutOptions.mm diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.h b/AsyncDisplayKit/Layout/ASLayoutOptions.h index 937f6c7640..022bbb61f3 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.h +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.h @@ -25,19 +25,9 @@ - (void)setValuesFromLayoutable:(id)layoutable; #pragma mark - Subclasses should implement these! -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor; - (void)setupDefaults; -- (instancetype)copyWithZone:(NSZone *)zone; - (void)copyIntoOptions:(ASLayoutOptions *)layoutOptions; -#pragma mark - Mutability checks - -@property (nonatomic, assign) BOOL isMutable; - -#if DEBUG -@property (nonatomic, assign) NSUInteger changeMonitor; -#endif - #pragma mark - ASStackLayoutable @property (nonatomic, readwrite) CGFloat spacingBefore; diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.m b/AsyncDisplayKit/Layout/ASLayoutOptions.m deleted file mode 100644 index 9d8d9c6a0e..0000000000 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.m +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2014-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - */ - -#import "ASLayoutOptions.h" - -#import -#import -#import "ASInternalHelpers.h" - -@implementation ASLayoutOptions - -static Class gDefaultLayoutOptionsClass = nil; -+ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass -{ - gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; -} - -+ (Class)defaultLayoutOptionsClass -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - if (gDefaultLayoutOptionsClass == nil) { - // If someone is asking for this and it hasn't been customized yet, use the default. - gDefaultLayoutOptionsClass = [ASLayoutOptions class]; - } - }); - return gDefaultLayoutOptionsClass; -} - -- (instancetype)init -{ - return [self initWithLayoutable:nil]; -} - -- (instancetype)initWithLayoutable:(id)layoutable; -{ - self = [super init]; - if (self) { - [self setupDefaults]; - [self setValuesFromLayoutable:layoutable]; - _isMutable = YES; -#if DEBUG - [self addObserver:self - forKeyPath:@"changeMonitor" - options:NSKeyValueObservingOptionNew - context:nil]; -#endif - } - return self; -} - -- (void)dealloc -{ -#if DEBUG - [self removeObserver:self forKeyPath:@"changeMonitor"]; -#endif -} - -#if DEBUG -+ (NSSet *)keyPathsForValuesAffectingChangeMonitor -{ - NSMutableSet *keys = [NSMutableSet set]; - [keys addObjectsFromArray:@[@"spacingBefore", @"spacingAfter", @"flexGrow", @"flexShrink", @"flexBasis", @"alignSelf"]]; - [keys addObjectsFromArray:@[@"ascender", @"descender"]]; - [keys addObjectsFromArray:@[@"sizeRange", @"layoutPosition"]]; - - return keys; -} - -#endif - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context -{ -#if DEBUG - if ([keyPath isEqualToString:@"changeMonitor"]) { - ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable"); - } else -#endif - { - [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } -} - - -#pragma mark - NSCopying -- (id)copyWithZone:(NSZone *)zone -{ - ASLayoutOptions *copy = [[[self class] alloc] init]; - [self copyIntoOptions:copy]; - return copy; -} - -- (void)copyIntoOptions:(ASLayoutOptions *)copy -{ - copy.flexBasis = self.flexBasis; - copy.spacingAfter = self.spacingAfter; - copy.spacingBefore = self.spacingBefore; - copy.flexGrow = self.flexGrow; - copy.flexShrink = self.flexShrink; - - copy.ascender = self.ascender; - copy.descender = self.descender; - - copy.sizeRange = self.sizeRange; - copy.layoutPosition = self.layoutPosition; -} - - -#pragma mark - Defaults -- (void)setupDefaults -{ - _flexBasis = ASRelativeDimensionUnconstrained; - _spacingBefore = 0; - _spacingAfter = 0; - _flexGrow = NO; - _flexShrink = NO; - _alignSelf = ASStackLayoutAlignSelfAuto; - - _ascender = 0; - _descender = 0; - - _sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); - _layoutPosition = CGPointZero; -} - -// Do this here instead of in Node/Spec subclasses so that custom specs can set default values -- (void)setValuesFromLayoutable:(id)layoutable -{ - if ([layoutable isKindOfClass:[ASTextNode class]]) { - ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); - } - if ([layoutable isKindOfClass:[ASDisplayNode class]]) { - ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; - self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); - self.layoutPosition = displayNode.frame.origin; - } -} - - -@end diff --git a/AsyncDisplayKit/Layout/ASLayoutOptions.mm b/AsyncDisplayKit/Layout/ASLayoutOptions.mm new file mode 100644 index 0000000000..ad6d224426 --- /dev/null +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +#import "ASLayoutOptions.h" + +#import +#import +#import +#import "ASInternalHelpers.h" + +@interface ASLayoutOptions() +{ + ASDN::RecursiveMutex _propertyLock; +} +@end + +@implementation ASLayoutOptions + +@synthesize spacingBefore = _spacingBefore; +@synthesize spacingAfter = _spacingAfter; +@synthesize flexGrow = _flexGrow; +@synthesize flexShrink = _flexShrink; +@synthesize flexBasis = _flexBasis; +@synthesize alignSelf = _alignSelf; + +@synthesize ascender = _ascender; +@synthesize descender = _descender; + +@synthesize sizeRange = _sizeRange; +@synthesize layoutPosition = _layoutPosition; + +static Class gDefaultLayoutOptionsClass = nil; ++ (void)setDefaultLayoutOptionsClass:(Class)defaultLayoutOptionsClass +{ + gDefaultLayoutOptionsClass = defaultLayoutOptionsClass; +} + ++ (Class)defaultLayoutOptionsClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + if (gDefaultLayoutOptionsClass == nil) { + // If someone is asking for this and it hasn't been customized yet, use the default. + gDefaultLayoutOptionsClass = [ASLayoutOptions class]; + } + }); + return gDefaultLayoutOptionsClass; +} + +- (instancetype)init +{ + return [self initWithLayoutable:nil]; +} + +- (instancetype)initWithLayoutable:(id)layoutable; +{ + self = [super init]; + if (self) { + [self setupDefaults]; + [self setValuesFromLayoutable:layoutable]; + } + return self; +} + +#pragma mark - NSCopying +- (id)copyWithZone:(NSZone *)zone +{ + ASLayoutOptions *copy = [[[self class] alloc] init]; + [self copyIntoOptions:copy]; + return copy; +} + +- (void)copyIntoOptions:(ASLayoutOptions *)copy +{ + ASDN::MutexLocker l(_propertyLock); + copy.flexBasis = self.flexBasis; + copy.spacingAfter = self.spacingAfter; + copy.spacingBefore = self.spacingBefore; + copy.flexGrow = self.flexGrow; + copy.flexShrink = self.flexShrink; + + copy.ascender = self.ascender; + copy.descender = self.descender; + + copy.sizeRange = self.sizeRange; + copy.layoutPosition = self.layoutPosition; +} + + +#pragma mark - Defaults +- (void)setupDefaults +{ + self.flexBasis = ASRelativeDimensionUnconstrained; + self.spacingBefore = 0; + self.spacingAfter = 0; + self.flexGrow = NO; + self.flexShrink = NO; + self.alignSelf = ASStackLayoutAlignSelfAuto; + + self.ascender = 0; + self.descender = 0; + + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero)); + self.layoutPosition = CGPointZero; +} + +// Do this here instead of in Node/Spec subclasses so that custom specs can set default values +- (void)setValuesFromLayoutable:(id)layoutable +{ + ASDN::MutexLocker l(_propertyLock); + if ([layoutable isKindOfClass:[ASTextNode class]]) { + ASTextNode *textNode = (ASTextNode *)layoutable; + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } + if ([layoutable isKindOfClass:[ASDisplayNode class]]) { + ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; + self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize)); + self.layoutPosition = displayNode.frame.origin; + } +} + +- (CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingAfter; +} + +- (void)setSpacingAfter:(CGFloat)spacingAfter +{ + ASDN::MutexLocker l(_propertyLock); + _spacingAfter = spacingAfter; +} + +- (CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + return _spacingBefore; +} + +- (void)setSpacingBefore:(CGFloat)spacingBefore +{ + ASDN::MutexLocker l(_propertyLock); + _spacingBefore = spacingBefore; +} + +- (BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + return _flexGrow; +} + +- (void)setFlexGrow:(BOOL)flexGrow +{ + ASDN::MutexLocker l(_propertyLock); + _flexGrow = flexGrow; +} + +- (BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + return _flexShrink; +} + +- (void)setFlexShrink:(BOOL)flexShrink +{ + ASDN::MutexLocker l(_propertyLock); + _flexShrink = flexShrink; +} + +- (ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + return _flexBasis; +} + +- (void)setFlexBasis:(ASRelativeDimension)flexBasis +{ + ASDN::MutexLocker l(_propertyLock); + _flexBasis = flexBasis; +} + +- (ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + return _alignSelf; +} + +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf +{ + ASDN::MutexLocker l(_propertyLock); + _alignSelf = alignSelf; +} + +- (CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + return _ascender; +} + +- (void)setAscender:(CGFloat)ascender +{ + ASDN::MutexLocker l(_propertyLock); + _ascender = ascender; +} + +- (CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + return _descender; +} + +- (void)setDescender:(CGFloat)descender +{ + ASDN::MutexLocker l(_propertyLock); + _descender = descender; +} + +- (ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + return _sizeRange; +} + +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange +{ + ASDN::MutexLocker l(_propertyLock); + _sizeRange = sizeRange; +} + +- (CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + return _layoutPosition; +} + +- (void)setLayoutPosition:(CGPoint)layoutPosition +{ + ASDN::MutexLocker l(_propertyLock); + _layoutPosition = layoutPosition; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 6c506b655c..0985014b80 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -62,12 +62,10 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey"; - (id)layoutableToAddFromLayoutable:(id)child { ASLayoutOptions *layoutOptions = [child layoutOptions]; - layoutOptions.isMutable = NO; id finalLayoutable = [child finalLayoutableWithParent:self]; if (finalLayoutable) { [layoutOptions copyIntoOptions:finalLayoutable.layoutOptions]; - finalLayoutable.layoutOptions.isMutable = NO; return finalLayoutable; } return child; From fe4a2d272f17bccd0bb48f35120bba1e85388f53 Mon Sep 17 00:00:00 2001 From: rcancro <@pinterest.com> Date: Tue, 8 Sep 2015 09:48:59 -0700 Subject: [PATCH 126/145] 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 b62dc5f963..e2e2e1e87e 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 ad6d224426..9e499bd172 100644 --- a/AsyncDisplayKit/Layout/ASLayoutOptions.mm +++ b/AsyncDisplayKit/Layout/ASLayoutOptions.mm @@ -117,8 +117,10 @@ static Class gDefaultLayoutOptionsClass = nil; ASDN::MutexLocker l(_propertyLock); if ([layoutable isKindOfClass:[ASTextNode class]]) { ASTextNode *textNode = (ASTextNode *)layoutable; - self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); - self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + if (textNode.attributedString.length > 0) { + self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale(); + self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale(); + } } if ([layoutable isKindOfClass:[ASDisplayNode class]]) { ASDisplayNode *displayNode = (ASDisplayNode *)layoutable; From 6383b690296422b620426bf266ee6c00859e91f0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 8 Sep 2015 22:30:56 +0300 Subject: [PATCH 127/145] 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 4a23a65916..39844d6a73 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 128/145] 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 33edd6ed7c..cf54d6fbde 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 8948591ddb..ff482d9c8d 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 129/145] 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 4dcf41e26e..ce40be7163 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 0985014b80..ac8e729371 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 2d317fc7ca..70865dbd02 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 130/145] 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 ac8e729371..412f194d27 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 131/145] 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 412f194d27..dfcd338a95 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 70865dbd02..a4aecf3a19 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 132/145] 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 e2e2e1e87e..22f2808c07 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 d147c76700..0000000000 --- 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 50139a548b..0000000000 --- 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 7e52b2c056..0000000000 --- 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 022bbb61f3..36058439f3 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 38644c0b60..8d46caf2f6 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 d7737f7eab..82d1aa0daf 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 b5a126c5cb..dd331f1405 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 ef109d47ae..afcb996425 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 e26d0a0210..5b1984929a 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 e3857d5ed4..6f208e1b29 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 bf01ff179e..aad53bad80 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 33895b9bd5..2b073c106a 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 95b445043c..549e150876 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 133/145] 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 8e9e12e379..90aecd3865 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 ea18aca990..bbeac0b882 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 58dc55f962..5e81e1f3d2 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 cc6ec9411a..960360f8c4 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 36058439f3..eddb5dae03 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 9e499bd172..ac4263825c 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 b00e09580a..34a26da4f2 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 39d129ba11..64b3f7b629 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 f132d5d7f5..1c3c901a32 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 dfcd338a95..c01af7df46 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 8d46caf2f6..7e0e950d3f 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 b71375f5be..1fdfd5fc28 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 64e9e2c118..eac7464aa3 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 afcb996425..3f0abf42fa 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 e9f47c2ecd..2be71b9320 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 aad53bad80..4e81dd60b5 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 549e150876..4d23cf6786 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 7514507a01..ab2eb3aac2 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 45e167afd7..10c129f10c 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 fbd893940b..2252078da6 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 134/145] 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 eddb5dae03..5e64573138 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 ac4263825c..b89b59ca85 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 c01af7df46..a91ca10372 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 135/145] 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 6d124b6e0a..c0489b2297 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 d4527c04f3..da000eaf33 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 2473f302b2..9d5bbe57f0 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 5e64573138..15d2f1dd89 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 136/145] 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 14b304086b..04543ff880 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 7fcfbd5999..ca6823317b 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 90aecd3865..5f9985802a 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 37196fb2f7..51d2b040e3 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 cf90d2f7fa..09167073e8 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 68f4f1fdab..85059a35f5 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 2ea1960290..e4abd526b8 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 137/145] 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 04543ff880..80bc34598c 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 ca6823317b..f8a346bd9a 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 5f9985802a..323ade43e5 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 51d2b040e3..c2ecde2b95 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 09167073e8..dce8d0f076 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 85059a35f5..d688de0deb 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 e4abd526b8..7066159c68 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 138/145] 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 15d2f1dd89..74352a34cf 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 b89b59ca85..67481cae02 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 a91ca10372..46e56dff4a 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 a4aecf3a19..f52dd54ad6 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 5b1984929a..3ebc9304f0 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 f2e565a7a9..e0325cfc05 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 139/145] 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 f8a346bd9a..11a138ea4f 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 140/145] 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 22f2808c07..1a032c42bb 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 2be71b9320..8e8435c9cc 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 141/145] 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 22f2808c07..1a032c42bb 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 2be71b9320..8e8435c9cc 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 142/145] 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 1a032c42bb..cb47886411 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 143/145] 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 da000eaf33..ed3ecd6212 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 ff482d9c8d..530b768768 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 39844d6a73..1d074ac317 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 144/145] 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 1c3c901a32..2dd00198ff 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 145/145] 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 530b768768..b7ebe9c2f1 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]; }