diff --git a/.gitignore b/.gitignore index 253f8380d5..7669d5a445 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ build *.gcov *.gcno *.gcda + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index bdcdb556ce..f5dba4322d 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -59,7 +59,7 @@ Pod::Spec.new do |spec| spec.subspec 'PINRemoteImage' do |pin| pin.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) PIN_REMOTE_IMAGE=1' } - pin.dependency 'PINRemoteImage/iOS', '>= 3.0.0-beta.3' + pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.5' pin.dependency 'AsyncDisplayKit/Core' end diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 5b6bcdd74f..b2b98afc59 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -199,6 +199,7 @@ 6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; 6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; }; 695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */; }; + 695BE2551DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */; }; 696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */; }; 69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */; }; 69708BA71D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; }; @@ -980,6 +981,7 @@ 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = ""; }; 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = ""; }; 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = ""; }; + 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWrapperSpecSnapshotTests.mm; sourceTree = ""; }; 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBackgroundLayoutSpecSnapshotTests.mm; sourceTree = ""; }; 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = ""; }; 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = ""; }; @@ -1108,8 +1110,8 @@ BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = ""; }; CC051F1E1D7A286A006434CB /* ASCALayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCALayerTests.m; sourceTree = ""; }; CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASUICollectionViewTests.m; sourceTree = ""; }; - CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = ""; }; CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageNodeTests.m; sourceTree = ""; }; + CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = ""; }; CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = ""; }; CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = ""; }; CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = ""; }; @@ -1429,6 +1431,7 @@ 052EE06A1A15A0D8002C6279 /* TestResources */, 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */, 69FEE53C1D95A9AF0086F066 /* ASLayoutElementStyleTests.m */, + 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */, ); path = AsyncDisplayKitTests; sourceTree = ""; @@ -2262,6 +2265,7 @@ CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */, F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.m in Sources */, ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */, + 695BE2551DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm in Sources */, CCA221D31D6FA7EF00AF6A0F /* ASViewControllerTests.m in Sources */, 058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */, 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */, diff --git a/AsyncDisplayKit/ASCellNode+Internal.h b/AsyncDisplayKit/ASCellNode+Internal.h index 0bf032b0fb..99066aa123 100644 --- a/AsyncDisplayKit/ASCellNode+Internal.h +++ b/AsyncDisplayKit/ASCellNode+Internal.h @@ -63,6 +63,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath; +@property (weak, nonatomic, nullable) ASDisplayNode *owningNode; + @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/ASCellNode.h b/AsyncDisplayKit/ASCellNode.h index 003e19da2e..0a6ccf3fb3 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/AsyncDisplayKit/ASCellNode.h @@ -108,6 +108,20 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { */ @property (nonatomic, assign, getter=isHighlighted) BOOL highlighted; +/** + * The current index path of this cell node, or @c nil if this node is + * not a valid item inside a table node or collection node. + * + * @note This property must be accessed on the main thread. + */ +@property (nonatomic, readonly, nullable) NSIndexPath *indexPath; + +/** + * The owning node (ASCollectionNode/ASTableNode) of this cell node, or @c nil if this node is + * not a valid item inside a table node or collection node or if those nodes are nil. + */ +@property (weak, nonatomic, readonly, nullable) ASDisplayNode *owningNode; + /* * ASCellNode must forward touch events in order for UITableView and UICollectionView tap handling to work. Overriding * these methods (e.g. for highlighting) requires the super method be called. diff --git a/AsyncDisplayKit/ASCellNode.mm b/AsyncDisplayKit/ASCellNode.mm index 2e14030c92..84c81b6bf6 100644 --- a/AsyncDisplayKit/ASCellNode.mm +++ b/AsyncDisplayKit/ASCellNode.mm @@ -36,6 +36,12 @@ ASDisplayNode *_viewControllerNode; UIViewController *_viewController; BOOL _suspendInteractionDelegate; + + struct { + unsigned int isTableNode:1; + unsigned int isCollectionNode:1; + } _owningNodeType; + } @end @@ -186,6 +192,19 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init } } +- (void)setOwningNode:(ASDisplayNode *)owningNode +{ + _owningNode = owningNode; + + memset(&_owningNodeType, 0, sizeof(_owningNodeType)); + + if ([owningNode isKindOfClass:[ASTableNode class]]) { + _owningNodeType.isTableNode = 1; + } else if ([owningNode isKindOfClass:[ASCollectionNode class]]) { + _owningNodeType.isCollectionNode = 1; + } +} + - (void)__setSelectedFromUIKit:(BOOL)selected; { if (selected != _selected) { @@ -204,6 +223,19 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init } } +- (NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + + if (_owningNodeType.isTableNode) { + return [(ASTableNode *)self.owningNode indexPathForNode:self]; + } else if (_owningNodeType.isCollectionNode) { + return [(ASCollectionNode *)self.owningNode indexPathForNode:self]; + } + + return nil; +} + #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-missing-super-calls" diff --git a/AsyncDisplayKit/ASCollectionNode+Beta.h b/AsyncDisplayKit/ASCollectionNode+Beta.h index cc2860099b..d1476c0baf 100644 --- a/AsyncDisplayKit/ASCollectionNode+Beta.h +++ b/AsyncDisplayKit/ASCollectionNode+Beta.h @@ -17,11 +17,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id)layoutFacilitator; -- (void)beginUpdates ASDISPLAYNODE_DEPRECATED; +- (void)beginUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); -- (void)endUpdatesAnimated:(BOOL)animated ASDISPLAYNODE_DEPRECATED; +- (void)endUpdatesAnimated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); -- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion ASDISPLAYNODE_DEPRECATED; +- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion ASDISPLAYNODE_DEPRECATED_MSG("Use -performBatchUpdates:completion: instead."); @end diff --git a/AsyncDisplayKit/ASCollectionNode.h b/AsyncDisplayKit/ASCollectionNode.h index b54d7ee79a..1c29a4b580 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/AsyncDisplayKit/ASCollectionNode.h @@ -74,6 +74,20 @@ NS_ASSUME_NONNULL_BEGIN */ @property (weak, nonatomic) id dataSource; +/** + * A Boolean value that indicates whether users can select items in the collection node. + * If the value of this property is YES (the default), users can select items. If you want more fine-grained control over the selection of items, you must provide a delegate object and implement the appropriate methods of the UICollectionNodeDelegate protocol. + */ +@property (nonatomic, assign) BOOL allowsSelection; + +/** + * A Boolean value that determines whether users can select more than one item in the collection node. + * This property controls whether multiple items can be selected simultaneously. The default value of this property is NO. + * When the value of this property is YES, tapping a cell adds it to the current selection (assuming the delegate permits the cell to be selected). Tapping the cell again removes it from the selection. + */ +@property (nonatomic, assign) BOOL allowsMultipleSelection; + + /** * Tuning parameters for a range type in full mode. * @@ -122,6 +136,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the item should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated; + #pragma mark - Editing /** @@ -159,6 +184,11 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion; +/** + * Blocks execution of the main thread until all section and item updates are committed to the view. This method must be called from the main thread. + */ +- (void)waitUntilAllUpdatesAreCommitted; + /** * Inserts one or more sections. * @@ -260,6 +290,43 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)reloadData; +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the collection view. + */ +- (void)relayoutItems; + +#pragma mark - Selection + +/** + * Selects the item at the specified index path and optionally scrolls it into view. + * If the `allowsSelection` property is NO, calling this method has no effect. If there is an existing selection with a different index path and the `allowsMultipleSelection` property is NO, calling this method replaces the previous selection. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath The index path of the item to select. Specifying nil for this parameter clears the current selection. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @param scrollPosition An option that specifies where the item should be positioned when scrolling finishes. For a list of possible values, see `UICollectionViewScrollPosition`. + * + * @discussion This method must be called from the main thread. + */ +- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition; + +/** + * Deselects the item at the specified index. + * If the allowsSelection property is NO, calling this method has no effect. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath The index path of the item to select. Specifying nil for this parameter clears the current selection. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @discussion This method must be called from the main thread. + */ +- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; + #pragma mark - Querying Data /** @@ -279,9 +346,9 @@ NS_ASSUME_NONNULL_BEGIN /** * Similar to -visibleCells. * - * @return an array containing the nodes being displayed on screen. + * @return an array containing the nodes being displayed on screen. This must be called on the main thread. */ -- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT; +@property(readonly, copy) NSArray<__kindof ASCellNode *> *visibleNodes; /** * Retrieves the node for the item at the given index path. @@ -301,6 +368,30 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; +/** + * Retrieve the index paths of all visible items. + * + * @return an array containing the index paths of all visible items. This must be called on the main thread. + */ +- (NSArray<__kindof NSIndexPath *> *)indexPathsForVisibleItems AS_WARN_UNUSED_RESULT; + +/** + * Retrieve the index path of the item at the given point. + * + * @param point The point of the requested item. + * + * @return The indexPath for the item at the given point. This must be called on the main thread. + */ +- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point AS_WARN_UNUSED_RESULT; + +/** + * Retrieve the cell at the given index path. + * + * @param indexPath The index path of the requested item. + * + * @return The cell for the given index path. This must be called on the main thread. + */ +- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath; /** * Retrieves the context object for the given section, as provided by the data source in @@ -328,7 +419,7 @@ NS_ASSUME_NONNULL_BEGIN * @deprecated This method is deprecated in 2.0. Use @c reloadDataWithCompletion: and * then @c waitUntilAllUpdatesAreCommitted instead. */ -- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED; +- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead."); @end @@ -410,7 +501,7 @@ NS_ASSUME_NONNULL_BEGIN * not implement reuse (it will be called once per row). Unlike UICollectionView's version, * this method is not called when the row is about to display. */ -- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Similar to -collectionView:nodeForItemAtIndexPath: @@ -424,7 +515,7 @@ NS_ASSUME_NONNULL_BEGIN * Must be thread-safe (can be called on the main thread or a background * queue) and should not implement reuse (it will be called once per row). */ -- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Asks the collection view to provide a supplementary node to display in the collection view. @@ -433,7 +524,7 @@ NS_ASSUME_NONNULL_BEGIN * @param kind The kind of supplementary node to provide. * @param indexPath The index path that specifies the location of the new supplementary node. */ -- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Indicator to lock the data source for data fetching in async mode. @@ -443,7 +534,7 @@ NS_ASSUME_NONNULL_BEGIN * @param collectionView The sender. * @deprecated The data source is always accessed on the main thread, and this method will not be called. */ -- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED; +- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); /** * Indicator to unlock the data source for data fetching in async mode. @@ -453,7 +544,7 @@ NS_ASSUME_NONNULL_BEGIN * @param collectionView The sender. * @deprecated The data source is always accessed on the main thread, and this method will not be called. */ -- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED; +- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); @end @@ -531,7 +622,7 @@ NS_ASSUME_NONNULL_BEGIN * * @return A constrained size range for layout the node at this index path. */ -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's constrainedSizeForItemAtIndexPath: instead."); /** * Informs the delegate that the collection view will add the given node @@ -545,7 +636,7 @@ NS_ASSUME_NONNULL_BEGIN * passed into this method may not correspond to the same item in your data source * if your data source has been updated since the last edit was processed. */ -- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Informs the delegate that the collection view did remove the provided node from the view hierarchy. @@ -560,9 +651,9 @@ NS_ASSUME_NONNULL_BEGIN * passed into this method may not correspond to the same item in your data source * if your data source has been updated since the last edit was processed. */ -- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); -- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED; +- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Tell the collectionView if batch fetching should begin. @@ -575,7 +666,7 @@ NS_ASSUME_NONNULL_BEGIN * If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching * should occur. */ -- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED; +- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); /** * Informs the delegate that the collection view will add the node @@ -590,7 +681,7 @@ NS_ASSUME_NONNULL_BEGIN * * This method is deprecated. Use @c collectionView:willDisplayNode:forItemAtIndexPath: instead. */ -- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); @end diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/AsyncDisplayKit/ASCollectionNode.mm index 664ca7cc13..4525e590fa 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/AsyncDisplayKit/ASCollectionNode.mm @@ -27,7 +27,9 @@ @interface _ASCollectionPendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; -@property (assign, nonatomic) ASLayoutRangeMode rangeMode; +@property (nonatomic, assign) ASLayoutRangeMode rangeMode; +@property (nonatomic, assign) BOOL allowsSelection; // default is YES +@property (nonatomic, assign) BOOL allowsMultipleSelection; // default is NO @end @implementation _ASCollectionPendingState @@ -37,6 +39,8 @@ self = [super init]; if (self) { _rangeMode = ASLayoutRangeModeCount; + _allowsSelection = YES; + _allowsMultipleSelection = NO; } return self; } @@ -143,9 +147,11 @@ if (_pendingState) { _ASCollectionPendingState *pendingState = _pendingState; - self.pendingState = nil; - view.asyncDelegate = pendingState.delegate; - view.asyncDataSource = pendingState.dataSource; + self.pendingState = nil; + view.asyncDelegate = pendingState.delegate; + view.asyncDataSource = pendingState.dataSource; + view.allowsSelection = pendingState.allowsSelection; + view.allowsMultipleSelection = pendingState.allowsMultipleSelection; if (pendingState.rangeMode != ASLayoutRangeModeCount) { [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; @@ -251,6 +257,44 @@ } } +- (void)setAllowsSelection:(BOOL)allowsSelection +{ + if ([self pendingState]) { + _pendingState.allowsSelection = allowsSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelection = allowsSelection; + } +} + +- (BOOL)allowsSelection +{ + if ([self pendingState]) { + return _pendingState.allowsSelection; + } else { + return self.view.allowsSelection; + } +} + +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelection = allowsMultipleSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASCollectionNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelection = allowsMultipleSelection; + } +} + +- (BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelection; + } else { + return self.view.allowsMultipleSelection; + } +} + #pragma mark - Range Tuning - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType @@ -273,21 +317,81 @@ return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; } +#pragma mark - Selection + +- (void)selectItemAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView selectItemAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition]; + } else { + NSLog(@"Failed to select item at index path %@ because the item never reached the view.", indexPath); + } +} + +- (void)deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView deselectItemAtIndexPath:indexPath animated:animated]; + } else { + NSLog(@"Failed to deselect item at index path %@ because the item never reached the view.", indexPath); + } +} + +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to item at index path %@ because the item never reached the view.", indexPath); + } +} + #pragma mark - Querying Data +- (void)reloadDataInitiallyIfNeeded +{ + if (!self.dataController.initialReloadDataHasBeenCalled) { + [self reloadData]; + } +} + - (NSInteger)numberOfItemsInSection:(NSInteger)section { + [self reloadDataInitiallyIfNeeded]; return [self.dataController numberOfRowsInSection:section]; } - (NSInteger)numberOfSections { + [self reloadDataInitiallyIfNeeded]; return [self.dataController numberOfSections]; } - (NSArray<__kindof ASCellNode *> *)visibleNodes { - return [self.view visibleNodes]; + ASDisplayNodeAssertMainThread(); + return self.isNodeLoaded ? [self.view visibleNodes] : @[]; +} + +- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + [self reloadDataInitiallyIfNeeded]; + return [self.dataController nodeAtIndexPath:indexPath]; } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode @@ -295,9 +399,41 @@ return [self.dataController indexPathForNode:cellNode]; } -- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +- (NSArray *)indexPathsForVisibleItems { - return [self.dataController nodeAtIndexPath:indexPath]; + ASDisplayNodeAssertMainThread(); + NSMutableArray *indexPathsArray = [NSMutableArray new]; + for (ASCellNode *cell in [self visibleNodes]) { + NSIndexPath *indexPath = [self indexPathForNode:cell]; + if (indexPath) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; +} + +- (nullable NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + NSIndexPath *indexPath = [collectionView indexPathForItemAtPoint:point]; + if (indexPath != nil) { + return [collectionView convertIndexPathToCollectionNode:indexPath]; + } + return indexPath; +} + +- (nullable UICollectionViewCell *)cellForItemAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASCollectionView *collectionView = self.view; + + indexPath = [collectionView convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; + if (indexPath == nil) { + return nil; + } + return [collectionView cellForItemAtIndexPath:indexPath]; } - (id)contextForSection:(NSInteger)section @@ -323,6 +459,11 @@ [self.view performBatchUpdates:updates completion:completion]; } +- (void)waitUntilAllUpdatesAreCommitted +{ + [self.view waitUntilAllUpdatesAreCommitted]; +} + - (void)reloadDataWithCompletion:(void (^)())completion { [self.view reloadDataWithCompletion:completion]; @@ -333,6 +474,11 @@ [self.view reloadData]; } +- (void)relayoutItems +{ + [self.view relayoutItems]; +} + - (void)reloadDataImmediately { [self.view reloadDataImmediately]; diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index bb0da3c704..b6c0b73e5f 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -145,7 +145,7 @@ NS_ASSUME_NONNULL_BEGIN * * @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. */ -- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED; +- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode instead of ASCollectionView."); /** * Initializes an ASCollectionView @@ -155,7 +155,7 @@ NS_ASSUME_NONNULL_BEGIN * @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. */ -- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED; +- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED_MSG("Please use ASCollectionNode instead of ASCollectionView."); /** * Tuning parameters for a range type in full mode. @@ -167,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Set the tuning parameters for a range type in full mode. @@ -178,7 +178,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Tuning parameters for a range type in the specified mode. @@ -191,7 +191,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Set the tuning parameters for a range type in the specified mode. @@ -203,7 +203,18 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +/** + * Scrolls the collection to the given item. + * + * @param indexPath The index path of the item. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); + +- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. @@ -215,7 +226,7 @@ NS_ASSUME_NONNULL_BEGIN * 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:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED; +- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Perform a batch of updates asynchronously. This method must be called from the main thread. @@ -226,7 +237,7 @@ NS_ASSUME_NONNULL_BEGIN * 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:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED; +- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Reload everything from scratch, destroying the working range and all cached nodes. @@ -235,14 +246,14 @@ NS_ASSUME_NONNULL_BEGIN * the main thread. * @warning This method is substantially more expensive than UICollectionView's version. */ -- (void)reloadDataWithCompletion:(nullable void (^)())completion ASDISPLAYNODE_DEPRECATED; +- (void)reloadDataWithCompletion:(nullable void (^)())completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Reload everything from scratch, destroying the working range and all cached nodes. * * @warning This method is substantially more expensive than UICollectionView's version. */ -- (void)reloadData ASDISPLAYNODE_DEPRECATED; +- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. @@ -250,19 +261,19 @@ NS_ASSUME_NONNULL_BEGIN * @warning This method is substantially more expensive than UICollectionView's version and will block the main thread * while all the cells load. */ -- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED; +- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's -reloadDataWithCompletion: followed by -waitUntilAllUpdatesAreCommitted instead."); /** * Triggers a relayout of all nodes. * * @discussion This method invalidates and lays out every cell node in the collection. */ -- (void)relayoutItems ASDISPLAYNODE_DEPRECATED; +- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread. */ -- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED; +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Registers the given kind of supplementary node for use in creating node-backed supplementary views. @@ -274,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN * methods. This method will register an internal backing view that will host the contents of the supplementary nodes * returned from the data source. */ -- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind ASDISPLAYNODE_DEPRECATED; +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Inserts one or more sections. @@ -284,7 +295,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)insertSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Deletes one or more sections. @@ -294,7 +305,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)deleteSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Reloads the specified sections. @@ -304,7 +315,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)reloadSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Moves a section to a new location. @@ -316,7 +327,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Inserts items at the locations identified by an array of index paths. @@ -326,7 +337,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Deletes the items specified by an array of index paths. @@ -336,7 +347,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Reloads the specified items. @@ -346,7 +357,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Moves the item at a specified location to a destination location. @@ -358,7 +369,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Query the sized node at @c indexPath for its calculatedSize. @@ -367,14 +378,14 @@ NS_ASSUME_NONNULL_BEGIN * * This method is deprecated. Call @c calculatedSize on the node of interest instead. First deprecated in version 2.0. */ -- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Call -calculatedSize on the node of interest instead."); /** * Similar to -visibleCells. * * @return an array containing the nodes being displayed on screen. */ -- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); /** * Similar to -indexPathForCell:. @@ -388,15 +399,15 @@ NS_ASSUME_NONNULL_BEGIN * for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the * collection node instead. */ -- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); @end -ASDISPLAYNODE_DEPRECATED +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDataSource.") @protocol ASCollectionViewDataSource @end -ASDISPLAYNODE_DEPRECATED +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDelegate.") @protocol ASCollectionViewDelegate @end @@ -416,7 +427,7 @@ ASDISPLAYNODE_DEPRECATED * `constrainedSizeForNodeAtIndexPath` * please file a github issue if you would like this to be restored. */ -- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section __deprecated_msg("This method does nothing for 1.9.7+ due to incorrect implementation previously, see the header file for more information."); +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("This method does nothing for 1.9.7+ due to incorrect implementation previously, see the header file for more information."); /** * Asks the delegate for the size of the header in the specified section. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index ded30340ea..969402d7ce 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -404,8 +404,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; super.dataSource = (id)_proxyDataSource; + //Cache results of layoutInspector to ensure flags are up to date if getter lazily loads a new one. + id layoutInspector = self.layoutInspector; if (_layoutInspectorFlags.didChangeCollectionViewDataSource) { - [self.layoutInspector didChangeCollectionViewDataSource:asyncDataSource]; + [layoutInspector didChangeCollectionViewDataSource:asyncDataSource]; } } @@ -465,8 +467,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; super.delegate = (id)_proxyDelegate; + //Cache results of layoutInspector to ensure flags are up to date if getter lazily loads a new one. + id layoutInspector = self.layoutInspector; if (_layoutInspectorFlags.didChangeCollectionViewDelegate) { - [self.layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; + [layoutInspector didChangeCollectionViewDelegate:asyncDelegate]; } } @@ -485,6 +489,11 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; { if (_layoutInspector == nil) { UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout; + if (layout == nil) { + // Layout hasn't been set yet, we're still init'ing + return nil; + } + if ([layout asdk_isFlowLayout]) { // Register the default layout inspector delegate for flow layouts only _defaultLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self flowLayout:layout]; @@ -546,19 +555,31 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - NSIndexPath *viewIndexPath = [self indexPathForNode:node]; - if (viewIndexPath == nil && wait) { - [self waitUntilAllUpdatesAreCommitted]; - viewIndexPath = [self indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.item == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + NSIndexPath *viewIndexPath = [self indexPathForNode:node]; + if (viewIndexPath == nil && wait) { + [self waitUntilAllUpdatesAreCommitted]; + viewIndexPath = [self indexPathForNode:node]; + } + return viewIndexPath; } - return viewIndexPath; } - (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath { - ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.item == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; + return [_dataController indexPathForNode:node]; + } } - (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath @@ -587,40 +608,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; return visibleNodes; } -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - */ -- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated -{ - ASDisplayNodeAssertMainThread(); - - NSIndexPath *viewIndexPath = [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; - if (viewIndexPath != nil) { - [super scrollToItemAtIndexPath:viewIndexPath atScrollPosition:scrollPosition animated:animated]; - } else { - NSLog(@"Warning: Ignoring request to scroll to item at index path %@ because the item did not reach the collection view.", indexPath); - } -} - -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - */ -- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition -{ - ASDisplayNodeAssertMainThread(); - - NSIndexPath *viewIndexPath = [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES]; - if (viewIndexPath != nil) { - [super selectItemAtIndexPath:viewIndexPath animated:animated scrollPosition:scrollPosition]; - } else { - NSLog(@"Warning: Ignoring request to select item at index path %@ because the item did not reach the collection view.", indexPath); - } -} - #pragma mark Internal /** @@ -1113,8 +1100,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (ASScrollDirection)scrollableDirections { + //Cache results of layoutInspector to ensure flags are up to date if getter lazily loads a new one. + id layoutInspector = self.layoutInspector; if (_layoutInspectorFlags.scrollableDirections) { - return [self.layoutInspector scrollableDirections]; + return [layoutInspector scrollableDirections]; } else { ASScrollDirection scrollableDirection = ASScrollDirectionNone; CGFloat totalContentWidth = self.contentSize.width + self.contentInset.left + self.contentInset.right; @@ -1224,7 +1213,11 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (void)_beginBatchFetching { [_batchContext beginBatchFetching]; - if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) { + if (_asyncDelegateFlags.collectionNodeWillBeginBatchFetch) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + [_asyncDelegate collectionNode:self.collectionNode willBeginBatchFetchWithContext:_batchContext]; + }); + } else if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" diff --git a/AsyncDisplayKit/ASCollectionViewProtocols.h b/AsyncDisplayKit/ASCollectionViewProtocols.h index f071887284..793d697744 100644 --- a/AsyncDisplayKit/ASCollectionViewProtocols.h +++ b/AsyncDisplayKit/ASCollectionViewProtocols.h @@ -19,11 +19,11 @@ NS_ASSUME_NONNULL_BEGIN @optional -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED; +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:numberOfItemsInSection: instead."); -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView ASDISPLAYNODE_DEPRECATED; +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView ASDISPLAYNODE_DEPRECATED_MSG("Implement -numberOfSectionsInCollectionNode: instead."); -- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement - collectionNode:viewForSupplementaryElementOfKind:atIndexPath: instead."); @end @@ -39,21 +39,21 @@ NS_ASSUME_NONNULL_BEGIN - (UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout; -- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:willDisplaySupplementaryView:forElementKind:atIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -collectionNode:didEndDisplayingSupplementaryView:forElementKind:atIndexPath: instead."); -- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldHighlightItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didHighlightItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didUnhighlightItemAtIndexPath: instead."); -- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldSelectItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didSelectItemAtIndexPath: instead."); +- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldDeselectItemAtIndexPath: instead."); +- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:didDeselectItemAtIndexPath: instead."); -- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED; -- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED; +- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:shouldShowMenuForItemAtIndexPath: instead."); +- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:canPerformAction:forItemAtIndexPath:withSender: instead."); +- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement collectionNode:performAction:forItemAtIndexPath:withSender: instead."); @end diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/AsyncDisplayKit/ASDisplayNode+Beta.h index 40ff9e10e5..e68d8827ee 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/AsyncDisplayKit/ASDisplayNode+Beta.h @@ -63,9 +63,6 @@ typedef struct { + (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT; + (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses; -/** @name Layout */ - - /** * @abstract Recursively ensures node and all subnodes are displayed. * @see Full documentation in ASDisplayNode+FrameworkPrivate.h @@ -97,8 +94,6 @@ typedef struct { */ @property (nonatomic, assign, readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements; -/** @name Layout Transitioning */ - /** * @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network. * Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished. diff --git a/AsyncDisplayKit/ASDisplayNode+Deprecated.h b/AsyncDisplayKit/ASDisplayNode+Deprecated.h index e62dd4769b..37904690a9 100644 --- a/AsyncDisplayKit/ASDisplayNode+Deprecated.h +++ b/AsyncDisplayKit/ASDisplayNode+Deprecated.h @@ -14,6 +14,14 @@ @interface ASDisplayNode (Deprecated) +/** + * @abstract The name of this node, which will be displayed in `description`. The default value is nil. + * + * @deprecated Deprecated in version 2.0: Use .debugName instead. This value will display in + * results of the -asciiArtString method (@see ASLayoutElementAsciiArtProtocol). + */ +@property (nullable, nonatomic, copy) NSString *name ASDISPLAYNODE_DEPRECATED_MSG("Use .debugName instead."); + /** * @abstract Asks the node to measure and return the size that best fits its subnodes. * @@ -33,7 +41,7 @@ * * @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout */ -- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED; +- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout."); /** * @abstract Calculate a layout based on given size range. @@ -42,9 +50,9 @@ * * @return An ASLayout instance defining the layout of the receiver and its children. * - * @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead + * @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout() or ASCalculateLayout() instead */ -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED; +- (nonnull ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: instead."); /** * @abstract Called whenever the visiblity of the node changed. @@ -53,16 +61,16 @@ * * @deprecated @see didEnterVisibleState @see didExitVisibleState */ -- (void)visibilityDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED; +- (void)visibilityDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead."); /** * @abstract Called whenever the visiblity of the node changed. * * @discussion Subclasses may use this to monitor when they become visible. * - * @deprecated @see didEnterVisibleState @see didExitVisibleStat + * @deprecated @see didEnterVisibleState @see didExitVisibleState */ -- (void)visibleStateDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED; +- (void)visibleStateDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterVisibleState / -didExitVisibleState instead."); /** * @abstract Called whenever the the node has entered or exited the display state. @@ -73,7 +81,7 @@ * * @deprecated @see didEnterDisplayState @see didExitDisplayState */ -- (void)displayStateDidChange:(BOOL)inDisplayState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED; +- (void)displayStateDidChange:(BOOL)inDisplayState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterDisplayState / -didExitDisplayState instead."); /** * @abstract Called whenever the the node has entered or left the load state. @@ -84,14 +92,14 @@ * * @deprecated @see didEnterPreloadState @see didExitPreloadState */ -- (void)loadStateDidChange:(BOOL)inLoadState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED; +- (void)loadStateDidChange:(BOOL)inLoadState ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use -didEnterPreloadState / -didExitPreloadState instead."); /** * @abstract Cancels all performing layout transitions. Can be called on any thread. * * @deprecated Deprecated in version 2.0: Use cancelLayoutTransition */ -- (void)cancelLayoutTransitionsInProgress ASDISPLAYNODE_DEPRECATED; +- (void)cancelLayoutTransitionsInProgress ASDISPLAYNODE_DEPRECATED_MSG("Use -cancelLayoutTransition instead."); /** * @abstract A boolean that shows whether the node automatically inserts and removes nodes based on the presence or @@ -102,6 +110,6 @@ * * @deprecated Deprecated in version 2.0: Use automaticallyManagesSubnodes */ -@property (nonatomic, assign) BOOL usesImplicitHierarchyManagement ASDISPLAYNODE_DEPRECATED; +@property (nonatomic, assign) BOOL usesImplicitHierarchyManagement ASDISPLAYNODE_DEPRECATED_MSG("Set .automaticallyManagesSubnodes instead."); @end diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index 4b6a7be8cd..d82353e69c 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -41,6 +41,7 @@ NS_ASSUME_NONNULL_BEGIN @interface ASDisplayNode (Subclassing) +#pragma mark - Properties /** @name Properties */ /** @@ -64,9 +65,9 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nullable, nonatomic, readonly, assign) ASLayout *calculatedLayout; +#pragma mark - View Lifecycle /** @name View Lifecycle */ - /** * @abstract Called on the main thread immediately after self.view is created. * @@ -75,9 +76,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)didLoad ASDISPLAYNODE_REQUIRES_SUPER; +#pragma mark - Layout /** @name Layout */ - /** * @abstract Called on the main thread by the view's -layoutSubviews. * @@ -101,6 +102,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)calculatedLayoutDidChange ASDISPLAYNODE_REQUIRES_SUPER; + +#pragma mark - Layout calculation /** @name Layout calculation */ /** @@ -159,9 +162,9 @@ NS_ASSUME_NONNULL_BEGIN * * @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead. * - * @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger - * an exception. A future version of the framework may support using both, calling them serially, with the - * .layoutSpecBlock superseding any values set by the method override. + * @warning Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an + * exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock + * superseding any values set by the method override. */ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize; @@ -174,9 +177,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)invalidateCalculatedLayout; +#pragma mark - Drawing /** @name Drawing */ - /** * @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set * to an appropriate context. @@ -407,9 +410,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay; +#pragma mark - Touch handling /** @name Touch handling */ - /** * @abstract Tells the node when touches began in its view. * @@ -443,9 +446,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)touchesCancelled:(nullable NSSet *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER; +#pragma mark - Managing Gesture Recognizers /** @name Managing Gesture Recognizers */ - /** * @abstract Asks the node if a gesture recognizer should continue tracking touches. * @@ -454,8 +457,9 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer; -/** @name Hit Testing */ +#pragma mark - Hit Testing +/** @name Hit Testing */ /** * @abstract Returns the view that contains the point. @@ -472,6 +476,8 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; + +#pragma mark - Placeholders /** @name Placeholders */ /** @@ -492,6 +498,7 @@ NS_ASSUME_NONNULL_BEGIN - (nullable UIImage *)placeholderImage; +#pragma mark - Description /** @name Description */ /** diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 3fbeb6a673..7be26c766e 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -175,13 +175,6 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (void)onDidLoad:(ASDisplayNodeDidLoadBlock)body; -/** @name Properties */ - -/** - * @abstract The name of this node, which will be displayed in `description`. The default value is nil. - */ -@property (nullable, nonatomic, copy) NSString *name; - /** * @abstract Returns whether the node is synchronous. * @@ -821,9 +814,33 @@ extern NSInteger const ASDefaultDrawingPriority; */ - (void)cancelLayoutTransition; +@end + +@interface ASDisplayNode (DeprecatedProtocolMethods) #pragma mark - Deprecated +/** + * @abstract Asks the node to measure and return the size that best fits its subnodes. + * + * @param constrainedSize The maximum size the receiver should fit in. + * + * @return A new size that fits the receiver's subviews. + * + * @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 calls -measureWithSizeRange: with zero min size. + * -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may + * be expensive if result is not cached. + * + * @see measureWithSizeRange: + * @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] + * + * @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout + */ +- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED; + /** * @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 @@ -834,7 +851,7 @@ extern NSInteger const ASDefaultDrawingPriority; * * @deprecated Deprecated in version 2.0: Just calls through to set the height and width property of the node. Convert to use sizing properties instead: height, minHeight, maxHeight, width, minWidth, maxWidth. */ -@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED; +@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED_MSG("Use .style.preferredSize instead OR set individual values with .style.height and .style.width."); @end diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index d73593ccd5..85ce357b83 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -30,10 +30,15 @@ #import "ASLayoutElementStylePrivate.h" #import "ASInternalHelpers.h" -#import "ASLayout.h" +#import "ASLayoutSpec+Subclasses.h" #import "ASLayoutSpec.h" #import "ASCellNode+Internal.h" #import "ASWeakProxy.h" +#import "ASLayoutSpecPrivate.h" + +#if DEBUG + #define AS_DEDUPE_LAYOUT_SPEC_TREE 1 +#endif NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority; NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes"; @@ -72,7 +77,7 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS @dynamic layoutElementType; -@synthesize name = _name; +@synthesize debugName = _debugName; @synthesize isFinalLayoutElement = _isFinalLayoutElement; @synthesize threadSafeBounds = _threadSafeBounds; @synthesize layoutSpecBlock = _layoutSpecBlock; @@ -181,10 +186,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // At most a layoutSpecBlock or one of the three layout methods is overridden #define __ASDisplayNodeCheckForLayoutMethodOverrides \ ASDisplayNodeAssert(_layoutSpecBlock != NULL || \ - (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + ((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \ + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \ - + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0) <= 1, \ - @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits, layoutSpecThatFits or calculateSizeThatFits", NSStringFromClass(self.class)) + + (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \ + @"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class)) + (void)initialize { @@ -194,14 +199,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // Subclasses should never override these. Use unused to prevent warnings __unused NSString *classString = NSStringFromClass(self); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString); ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", classString); - ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead overwrite calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead overwrite calculateLayoutThatFits:.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method.", classString); + ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method.", classString); } // Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values @@ -227,18 +232,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) #if DEBUG - // Check if subnodes where modified during layoutSpecThatFits: - if (self == [ASDisplayNode class] || ASSubclassOverridesSelector([ASDisplayNode class], self, @selector(layoutSpecThatFits:))) - { - __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(layoutSpecThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { + // Check if subnodes where modified during the creation of the layout + if (self == [ASDisplayNode class]) { + __block IMP originalLayoutSpecThatFitsIMP = ASReplaceMethodWithBlock(self, @selector(_layoutElementThatFits:), ^(ASDisplayNode *_self, ASSizeRange sizeRange) { NSArray *oldSubnodes = _self.subnodes; - ASLayoutSpec *layoutSpec = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(layoutSpecThatFits:), sizeRange); + ASLayoutSpec *layoutElement = ((ASLayoutSpec *( *)(id, SEL, ASSizeRange))originalLayoutSpecThatFitsIMP)(_self, @selector(_layoutElementThatFits:), sizeRange); NSArray *subnodes = _self.subnodes; - ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecThatFits: is verboten."); + ASDisplayNodeAssert(oldSubnodes.count == subnodes.count, @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); for (NSInteger i = 0; i < oldSubnodes.count; i++) { - ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding and removing nodes in layoutSpecThatFits: is verboten."); + ASDisplayNodeAssert(oldSubnodes[i] == subnodes[i], @"Adding or removing nodes in layoutSpecBlock or layoutSpecThatFits: is not allowed and can cause unexpected behavior."); } - return layoutSpec; + return layoutElement; }); } #endif @@ -672,17 +676,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return (_view != nil || (_layer != nil && _flags.layerBacked)); } -- (NSString *)name +- (NSString *)debugName { ASDN::MutexLocker l(__instanceLock__); - return _name; + return _debugName; } -- (void)setName:(NSString *)name +- (void)setDebugName:(NSString *)debugName { ASDN::MutexLocker l(__instanceLock__); - if (!ASObjectIsEqual(_name, name)) { - _name = [name copy]; + if (!ASObjectIsEqual(_debugName, debugName)) { + _debugName = [debugName copy]; } } @@ -1649,7 +1653,8 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD - (void)addSubnode:(ASDisplayNode *)subnode { - ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually add subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + // TODO: 2.0 Conversion: Reenable and fix within product code + //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually add subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); [self _addSubnode:subnode]; } @@ -1765,7 +1770,8 @@ static bool disableNotificationsForMovingBetweenParents(ASDisplayNode *from, ASD - (void)replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode { - ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually replace old node with replacement node to node with automaticallyManagesSubnodes=YES. Old Node: %@, replacement node: %@", oldSubnode, replacementSubnode); + // TODO: 2.0 Conversion: Reenable and fix within product code + //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually replace old node with replacement node to node with automaticallyManagesSubnodes=YES. Old Node: %@, replacement node: %@", oldSubnode, replacementSubnode); [self _replaceSubnode:oldSubnode withSubnode:replacementSubnode]; } @@ -1801,7 +1807,8 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below { - ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + // TODO: 2.0 Conversion: Reenable and fix within product code + //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); [self _insertSubnode:subnode belowSubnode:below]; } @@ -1852,7 +1859,8 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above { - ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + // TODO: 2.0 Conversion: Reenable and fix within product code + //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); [self _insertSubnode:subnode aboveSubnode:above]; } @@ -1906,7 +1914,8 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx { - ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); + // TODO: 2.0 Conversion: Reenable and fix within product code + //ASDisplayNodeAssert(self.automaticallyManagesSubnodes == NO, @"Attempt to manually insert subnode to node with automaticallyManagesSubnodes=YES. Node: %@", subnode); [self _insertSubnode:subnode atIndex:idx]; } @@ -1984,7 +1993,7 @@ static NSInteger incrementIfFound(NSInteger i) { - (void)removeFromSupernode { - ASDisplayNodeAssert(self.supernode.automaticallyManagesSubnodes == NO, @"Attempt to manually remove subnode from node with automaticallyManagesSubnodes=YES. Node: %@", self); + //ASDisplayNodeAssert(self.supernode.automaticallyManagesSubnodes == NO, @"Attempt to manually remove subnode from node with automaticallyManagesSubnodes=YES. Node: %@", self); [self _removeFromSupernode]; } @@ -2415,71 +2424,100 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) __ASDisplayNodeCheckForLayoutMethodOverrides; ASDN::MutexLocker l(__instanceLock__); - if ((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || _layoutSpecBlock != NULL) { - BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; - if (measureLayoutSpec) { - _layoutSpecNumberOfPasses++; - } - ASLayoutSpec *layoutSpec = ({ - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); - [self layoutSpecThatFits:constrainedSize]; - }); - - ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); - - layoutSpec.parent = self; // This causes upward propogation of any non-default layoutElement values. - - // manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection - { - ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); - ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection); - } - - layoutSpec.isMutable = NO; - BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; - if (measureLayoutComputation) { - _layoutComputationNumberOfPasses++; - } - - ASLayout *layout = ({ - ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); - [layoutSpec layoutThatFits:constrainedSize]; - }); - - ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec); - - // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct. - BOOL isFinalLayoutElement = (layout.layoutElement != self); - if (isFinalLayoutElement) { - layout.position = CGPointZero; - layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]]; - } - ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); - return [layout filteredNodeLayoutTree]; - } else { + // Manual size calculation via calculateSizeThatFits: + if (((_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) || + (_layoutSpecBlock != NULL)) == NO) { CGSize size = [self calculateSizeThatFits:constrainedSize.max]; ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size)); return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil]; } + + // Size calcualtion with layout elements + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (measureLayoutSpec) { + _layoutSpecNumberOfPasses++; + } + + // Get layout element from the node + id layoutElement = [self _layoutElementThatFits:constrainedSize]; + + // Certain properties are necessary to set on an element of type ASLayoutSpec + if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) { + ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement; + + NSSet *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree]; + if (duplicateElements.count > 0) { + ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements); + // Use an empty layout spec to avoid crashes + layoutSpec = [[ASLayoutSpec alloc] init]; + } + + ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec); + layoutSpec.parent = self; + layoutSpec.isMutable = NO; + } + + // Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection + { + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + ASEnvironmentStatePropagateDown(layoutElement, [self environmentTraitCollection]); + } + + BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation; + if (measureLayoutComputation) { + _layoutComputationNumberOfPasses++; + } + + // Layout element layout creation + ASLayout *layout = ({ + ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation); + [layoutElement layoutThatFits:constrainedSize]; + }); + ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout); + + // Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct. + BOOL isFinalLayoutElement = (layout.layoutElement != self); + if (isFinalLayoutElement) { + layout.position = CGPointZero; + layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]]; + } + ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout); + + return [layout filteredNodeLayoutTree]; } - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { __ASDisplayNodeCheckForLayoutMethodOverrides; + + ASDisplayNodeAssert(ASIsCGSizeValidForSize(constrainedSize), @"Cannot calculate size of node because constrained size is infinite and node does not override -calculateSizeThatFits:. Try setting style.preferredSize on the node. Node: %@", self); - return CGSizeZero; + return constrainedSize; +} + +- (id)_layoutElementThatFits:(ASSizeRange)constrainedSize +{ + __ASDisplayNodeCheckForLayoutMethodOverrides; + + BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec; + if (_layoutSpecBlock != NULL) { + return ({ + ASDN::MutexLocker l(__instanceLock__); + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + _layoutSpecBlock(self, constrainedSize); + }); + } else { + return ({ + ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec); + [self layoutSpecThatFits:constrainedSize]; + }); + } } - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { __ASDisplayNodeCheckForLayoutMethodOverrides; - - ASDN::MutexLocker l(__instanceLock__); - - if (_layoutSpecBlock != NULL) { - return _layoutSpecBlock(self, constrainedSize); - } ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported."); return [[ASLayoutSpec alloc] init]; @@ -2487,11 +2525,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) - (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock { - // For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided + // For now there should never be an overwrite of layoutSpecThatFits: / layoutElementThatFits: and a layoutSpecBlock ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported"); ASDN::MutexLocker l(__instanceLock__); - _layoutSpecBlock = [layoutSpecBlock copy]; + _layoutSpecBlock = layoutSpecBlock; } - (ASLayoutSpecBlock)layoutSpecBlock @@ -3285,8 +3323,8 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; - (NSMutableArray *)propertiesForDescription { NSMutableArray *result = [NSMutableArray array]; - if (self.name.length > 0) { - [result addObject:@{ @"name" : ASStringWithQuotesIfMultiword(self.name) }]; + if (self.debugName.length > 0) { + [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName) }]; } return result; } @@ -3295,8 +3333,8 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; { NSMutableArray *result = [NSMutableArray array]; - if (self.name.length > 0) { - [result addObject:@{ @"name" : ASStringWithQuotesIfMultiword(self.name)}]; + if (self.debugName.length > 0) { + [result addObject:@{ @"debugName" : ASStringWithQuotesIfMultiword(self.debugName)}]; } CGRect windowFrame = [self _frameInWindow]; @@ -3402,11 +3440,6 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; return self.subnodes; } -- (BOOL)supportsUpwardPropagation -{ - return ASEnvironmentStatePropagationEnabled(); -} - - (BOOL)supportsTraitsCollectionPropagation { return ASEnvironmentStateTraitCollectionPropagationEnabled(); @@ -3437,6 +3470,15 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; // Subclass override } +#pragma mark - Deprecated + +ASLayoutElementStyleForwarding + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; +} + ASEnvironmentLayoutExtensibilityForwarding #if TARGET_OS_TV @@ -3472,39 +3514,6 @@ ASEnvironmentLayoutExtensibilityForwarding } #endif -#pragma mark - Deprecated - -- (CGSize)measure:(CGSize)constrainedSize -{ - return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; -} - -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize -{ - return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; -} - -- (void)setPreferredFrameSize:(CGSize)preferredFrameSize -{ - ASDN::MutexLocker l(__instanceLock__); - - // Deprecated preferredFrameSize just calls through to set width and height - self.style.preferredSize = preferredFrameSize; - [self invalidateCalculatedLayout]; -} - -- (CGSize)preferredFrameSize -{ - ASDN::MutexLocker l(__instanceLock__); - - ASLayoutElementStyle *style = self.style; - if (style.width.unit == ASDimensionUnitPoints && style.height.unit == ASDimensionUnitPoints) { - return CGSizeMake(style.width.value, style.height.value); - } - - return CGSizeZero; -} - @end @implementation ASDisplayNode (Debugging) @@ -3537,12 +3546,16 @@ ASEnvironmentLayoutExtensibilityForwarding - (NSString *)asciiArtString { - return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; + return [ASLayoutSpec asciiArtStringForChildren:@[] parentName:[self asciiArtName]]; } - (NSString *)asciiArtName { - return NSStringFromClass([self class]); + NSString *string = NSStringFromClass([self class]); + if (_debugName) { + string = [string stringByAppendingString:[NSString stringWithFormat:@"\"%@\"",_debugName]]; + } + return string; } @end @@ -3616,9 +3629,39 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; @end +#pragma mark - Deprecated @implementation ASDisplayNode (Deprecated) +- (NSString *)name +{ + return self.debugName; +} + +- (void)setName:(NSString *)name +{ + self.debugName = name; +} + +- (CGSize)measure:(CGSize)constrainedSize +{ + return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size; +} + +- (void)setPreferredFrameSize:(CGSize)preferredFrameSize +{ + // Deprecated preferredFrameSize just calls through to set width and height + self.style.preferredSize = preferredFrameSize; + [self invalidateCalculatedLayout]; +} + +- (CGSize)preferredFrameSize +{ + ASLayoutSize size = self.style.preferredLayoutSize; + BOOL isPoints = (size.width.unit == ASDimensionUnitPoints && size.height.unit == ASDimensionUnitPoints); + return isPoints ? CGSizeMake(size.width.value, size.height.value) : CGSizeZero; +} + - (void)cancelLayoutTransitionsInProgress { [self cancelLayoutTransition]; diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index f78870aa7a..f58a8c769f 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -190,7 +190,7 @@ struct ASImageNodeDrawParameters { ASDN::MutexLocker l(__instanceLock__); if (_image == nil) { - return constrainedSize; + return [super calculateSizeThatFits:constrainedSize]; } return _image.size; @@ -530,7 +530,7 @@ static ASDN::Mutex cacheLock; // Stash the block and call-site queue. We'll invoke it in -displayDidFinish. ASDN::MutexLocker l(__instanceLock__); if (_displayCompletionBlock != displayCompletionBlock) { - _displayCompletionBlock = [displayCompletionBlock copy]; + _displayCompletionBlock = displayCompletionBlock; } [self setNeedsDisplay]; diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/AsyncDisplayKit/ASNetworkImageNode.mm index 73ccbebc03..8a989c5c8d 100755 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/AsyncDisplayKit/ASNetworkImageNode.mm @@ -331,23 +331,6 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; } } -#pragma mark - Layout and Sizing - -- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize -{ - ASDN::MutexLocker l(__instanceLock__); - - // If the image node is currently in the loading process and no valid size is applied return CGSizeZero for the time - // being. - // TODO: After 2.0 is stable we should remove this behavior as a ASNetworkImageNode is a replaced element and the - // client code should set the size of an image or it's container it's embedded in - if (ASIsCGSizeValidForSize(constrainedSize) == NO && _URL != nil && self.image == nil) { - return CGSizeZero; - } - - return [super calculateSizeThatFits:constrainedSize]; -} - #pragma mark - Private methods, safe to call without lock - (void)handleProgressImage:(UIImage *)progressImage progress:(CGFloat)progress downloadIdentifier:(nullable id)downloadIdentifier diff --git a/AsyncDisplayKit/ASPagerNode.m b/AsyncDisplayKit/ASPagerNode.m index 909d1089dc..15a6ccb975 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/AsyncDisplayKit/ASPagerNode.m @@ -14,7 +14,6 @@ #import "ASDelegateProxy.h" #import "ASDisplayNode+Subclasses.h" #import "ASPagerFlowLayout.h" -#import "ASCollectionView+Undeprecated.h" @interface ASPagerNode () { @@ -102,17 +101,17 @@ - (void)scrollToPageAtIndex:(NSInteger)index animated:(BOOL)animated { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; - [self.view scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; + [self scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:animated]; } - (ASCellNode *)nodeForPageAtIndex:(NSInteger)index { - return [self.view nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; + return [self nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; } #pragma mark - ASCollectionDataSource -- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load nodes to display"); if (!_pagerDataSourceImplementsNodeBlockAtIndex) { @@ -122,19 +121,21 @@ return [_pagerDataSource pagerNode:self nodeBlockAtIndex:indexPath.item]; } -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section { ASDisplayNodeAssert(_pagerDataSource != nil, @"ASPagerNode must have a data source to load nodes to display"); return [_pagerDataSource numberOfPagesInPagerNode:self]; } -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +#pragma mark - ASCollectionDelegate + +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath { if (_pagerDelegateImplementsConstrainedSizeForNode) { return [_pagerDelegate pagerNode:self constrainedSizeForNodeAtIndex:indexPath.item]; } - return ASSizeRangeMake(CGSizeZero, self.view.bounds.size); + return ASSizeRangeMake(CGSizeZero, self.bounds.size); } #pragma mark - Data Source Proxy diff --git a/AsyncDisplayKit/ASRunLoopQueue.mm b/AsyncDisplayKit/ASRunLoopQueue.mm index 42e5ea96e7..aac7ad1d17 100644 --- a/AsyncDisplayKit/ASRunLoopQueue.mm +++ b/AsyncDisplayKit/ASRunLoopQueue.mm @@ -164,7 +164,7 @@ static void runLoopSourceCallback(void *info) { if (self = [super init]) { _runLoop = runloop; _internalQueue = std::deque(); - _queueConsumer = [handlerBlock copy]; + _queueConsumer = handlerBlock; _batchSize = 1; _ensureExclusiveMembership = YES; diff --git a/AsyncDisplayKit/ASTableNode.h b/AsyncDisplayKit/ASTableNode.h index e1ed605855..e656f1b083 100644 --- a/AsyncDisplayKit/ASTableNode.h +++ b/AsyncDisplayKit/ASTableNode.h @@ -35,6 +35,27 @@ NS_ASSUME_NONNULL_BEGIN @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; +/* + * A Boolean value that determines whether users can select a row. + * If the value of this property is YES (the default), users can select rows. If you set it to NO, they cannot select rows. Setting this property affects cell selection only when the table view is not in editing mode. If you want to restrict selection of cells in editing mode, use `allowsSelectionDuringEditing`. + */ +@property (nonatomic, assign) BOOL allowsSelection; +/* + * A Boolean value that determines whether users can select cells while the table view is in editing mode. + * If the value of this property is YES, users can select rows during editing. The default value is NO. If you want to restrict selection of cells regardless of mode, use allowsSelection. + */ +@property (nonatomic, assign) BOOL allowsSelectionDuringEditing; +/* + * A Boolean value that determines whether users can select more than one row outside of editing mode. + * This property controls whether multiple rows can be selected simultaneously outside of editing mode. When the value of this property is YES, each row that is tapped acquires a selected appearance. Tapping the row again removes the selected appearance. If you access indexPathsForSelectedRows, you can get the index paths that identify the selected rows. + */ +@property (nonatomic, assign) BOOL allowsMultipleSelection; +/* + * A Boolean value that controls whether users can select more than one cell simultaneously in editing mode. + * The default value of this property is NO. If you set it to YES, check marks appear next to selected rows in editing mode. In addition, UITableView does not query for editing styles when it goes into editing mode. If you access indexPathsForSelectedRows, you can get the index paths that identify the selected rows. + */ +@property (nonatomic, assign) BOOL allowsMultipleSelectionDuringEditing; + /** * Tuning parameters for a range type in full mode. * @@ -83,6 +104,17 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType; +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + * + * This method must be called on the main thread. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated; + /** * Reload everything from scratch, destroying the working range and all cached nodes. * @@ -99,6 +131,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)reloadData; +/** + * Triggers a relayout of all nodes. + * + * @discussion This method invalidates and lays out every cell node in the table view. + */ +- (void)relayoutItems; + /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * The data source must be updated to reflect the changes before the update block completes. @@ -223,6 +262,37 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; +#pragma mark - Selection + +/** + * Selects a row in the table view identified by index path, optionally scrolling the row to a location in the table view. + * This method does not cause any selection-related delegate methods to be called. + * + * @param indexPath An index path identifying a row in the table view. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @param scrollPosition A constant that identifies a relative position in the table view (top, middle, bottom) for the row when scrolling concludes. See `UITableViewScrollPosition` for descriptions of valid constants. + * + * @discussion This method must be called from the main thread. + */ +- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition; + +/* + * Deselects a given row identified by index path, with an option to animate the deselection. + * This method does not cause any selection-related delegate methods to be called. + * Calling this method does not cause any scrolling to the deselected row. + * + * @param indexPath An index path identifying a row in the table view. + * + * @param animated Specify YES to animate the change in the selection or NO to make the change without animating it. + * + * @discussion This method must be called from the main thread. + */ +- (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated; + +#pragma mark - Querying Data + /** * Retrieves the number of rows in the given section. * @@ -256,6 +326,74 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; +/** + * Similar to -[UITableView rectForRowAtIndexPath:] + * + * @param indexPath An index path identifying a row in the table view. + * + * @return A rectangle defining the area in which the table view draws the row or CGRectZero if indexPath is invalid. + * + * @discussion This method must be called from the main thread. + */ +- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView cellForRowAtIndexPath:] + * + * @param indexPath An index path identifying a row in the table view. + * + * @return An object representing a cell of the table, or nil if the cell is not visible or indexPath is out of range. + * + * @discussion This method must be called from the main thread. + */ +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathForSelectedRow] + * + * @return The value of this property is an index path identifying the row and section + * indexes of the selected row, or nil if the index path is invalid. If there are multiple selections, + * this property contains the first index-path object in the array of row selections; + * this object has the lowest index values for section and row. + * + * @discussion This method must be called from the main thread. + */ +- (nullable NSIndexPath *)indexPathForSelectedRow AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathForRowAtPoint:] + * + * @param point A point in the local coordinate system of the table view (the table view’€™s bounds). + * + * @return An index path representing the row and section associated with point, + * or nil if the point is out of the bounds of any row. + * + * @discussion This method must be called from the main thread. + */ +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathsForRowsInRect:] + * + * @param rect A rectangle defining an area of the table view in local coordinates. + * + * @return An array of NSIndexPath objects each representing a row and section index identifying a row within rect. + * Returns an empty array if there aren’t any rows to return. + * + * @discussion This method must be called from the main thread. + */ +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect AS_WARN_UNUSED_RESULT; + +/** + * Similar to -[UITableView indexPathsForVisibleRows] + * + * @return The value of this property is an array of NSIndexPath objects each representing a row index and section index + * that together identify a visible row in the table view. If no rows are visible, the value is nil. + * + * @discussion This method must be called from the main thread. + */ +- (NSArray *)indexPathsForVisibleRows AS_WARN_UNUSED_RESULT; + @end /** @@ -279,6 +417,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section; +/** + * Similar to -visibleCells. + * + * @return an array containing the nodes being displayed on screen. This must be called on the main thread. + */ +@property(readonly, copy) NSArray<__kindof ASCellNode *> *visibleNodes; + /** * Asks the data source for a block to create a node to represent the row at the given index path. * The block will be run by the table node concurrently in the background before the row is inserted @@ -316,7 +461,7 @@ NS_ASSUME_NONNULL_BEGIN * @return a node for display at this indexpath. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method * is not called when the row is about to display. */ -- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Similar to -tableView:nodeForRowAtIndexPath: @@ -329,7 +474,7 @@ NS_ASSUME_NONNULL_BEGIN * Must be thread-safe (can be called on the main thread or a background * queue) and should not implement reuse (it will be called once per row). */ -- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Indicator to lock the data source for data fetching in async mode. @@ -339,7 +484,7 @@ NS_ASSUME_NONNULL_BEGIN * @param tableView The sender. * @deprecated The data source is always accessed on the main thread, and this method will not be called. */ -- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED; +- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); /** * Indicator to unlock the data source for data fetching in asyn mode. @@ -349,7 +494,7 @@ NS_ASSUME_NONNULL_BEGIN * @param tableView The sender. * @deprecated The data source is always accessed on the main thread, and this method will not be called. */ -- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED; +- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Data source accesses are on the main thread. Method will not be called."); @end @@ -434,7 +579,7 @@ NS_ASSUME_NONNULL_BEGIN * passed into this method may not correspond to the same item in your data source * if your data source has been updated since the last edit was processed. */ -- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Informs the delegate that the table view did remove the provided node from the view hierarchy. @@ -449,7 +594,7 @@ NS_ASSUME_NONNULL_BEGIN * passed into this method may not correspond to the same item in your data source * if your data source has been updated since the last edit was processed. */ -- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary. @@ -463,7 +608,7 @@ NS_ASSUME_NONNULL_BEGIN * ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a * UIRefreshControl. */ -- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED; +- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Tell the tableView if batch fetching should begin. @@ -476,7 +621,7 @@ NS_ASSUME_NONNULL_BEGIN * If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching * should occur. */ -- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Provides the constrained size range for measuring the row at the index path. @@ -488,7 +633,7 @@ NS_ASSUME_NONNULL_BEGIN * * @return A constrained size range for layout the node at this index path. */ -- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); /** * Informs the delegate that the table view will add the node @@ -503,7 +648,7 @@ NS_ASSUME_NONNULL_BEGIN * * This method is deprecated. Use @c tableView:willDisplayNode:forRowAtIndexPath: instead. */ -- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's method instead."); @end diff --git a/AsyncDisplayKit/ASTableNode.mm b/AsyncDisplayKit/ASTableNode.mm index d676775795..6fd58035a7 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/AsyncDisplayKit/ASTableNode.mm @@ -24,7 +24,11 @@ @interface _ASTablePendingState : NSObject @property (weak, nonatomic) id delegate; @property (weak, nonatomic) id dataSource; -@property (assign, nonatomic) ASLayoutRangeMode rangeMode; +@property (nonatomic, assign) ASLayoutRangeMode rangeMode; +@property (nonatomic, assign) BOOL allowsSelection; +@property (nonatomic, assign) BOOL allowsSelectionDuringEditing; +@property (nonatomic, assign) BOOL allowsMultipleSelection; +@property (nonatomic, assign) BOOL allowsMultipleSelectionDuringEditing; @end @implementation _ASTablePendingState @@ -33,6 +37,10 @@ self = [super init]; if (self) { _rangeMode = ASLayoutRangeModeCount; + _allowsSelection = YES; + _allowsSelectionDuringEditing = NO; + _allowsMultipleSelection = NO; + _allowsMultipleSelectionDuringEditing = NO; } return self; } @@ -104,6 +112,10 @@ self.pendingState = nil; view.asyncDelegate = pendingState.delegate; view.asyncDataSource = pendingState.dataSource; + view.allowsSelection = pendingState.allowsSelection; + view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing; + view.allowsMultipleSelection = pendingState.allowsMultipleSelection; + view.allowsMultipleSelectionDuringEditing = pendingState.allowsMultipleSelectionDuringEditing; if (pendingState.rangeMode != ASLayoutRangeModeCount) { [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } @@ -215,6 +227,82 @@ } } +- (void)setAllowsSelection:(BOOL)allowsSelection +{ + if ([self pendingState]) { + _pendingState.allowsSelection = allowsSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelection = allowsSelection; + } +} + +- (BOOL)allowsSelection +{ + if ([self pendingState]) { + return _pendingState.allowsSelection; + } else { + return self.view.allowsSelection; + } +} + +- (void)setAllowsSelectionDuringEditing:(BOOL)allowsSelectionDuringEditing +{ + if ([self pendingState]) { + _pendingState.allowsSelectionDuringEditing = allowsSelectionDuringEditing; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsSelectionDuringEditing = allowsSelectionDuringEditing; + } +} + +- (BOOL)allowsSelectionDuringEditing +{ + if ([self pendingState]) { + return _pendingState.allowsSelectionDuringEditing; + } else { + return self.view.allowsSelectionDuringEditing; + } +} + +- (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelection = allowsMultipleSelection; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelection = allowsMultipleSelection; + } +} + +- (BOOL)allowsMultipleSelection +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelection; + } else { + return self.view.allowsMultipleSelection; + } +} + +- (void)setAllowsMultipleSelectionDuringEditing:(BOOL)allowsMultipleSelectionDuringEditing +{ + if ([self pendingState]) { + _pendingState.allowsMultipleSelectionDuringEditing = allowsMultipleSelectionDuringEditing; + } else { + ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist"); + self.view.allowsMultipleSelectionDuringEditing = allowsMultipleSelectionDuringEditing; + } +} + +- (BOOL)allowsMultipleSelectionDuringEditing +{ + if ([self pendingState]) { + return _pendingState.allowsMultipleSelectionDuringEditing; + } else { + return self.view.allowsMultipleSelectionDuringEditing; + } +} + #pragma mark ASRangeControllerUpdateRangeProtocol - (void)updateCurrentRangeWithMode:(ASLayoutRangeMode)rangeMode @@ -253,18 +341,76 @@ ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType]; } +#pragma mark - Selection + +- (void)selectRowAtIndexPath:(nullable NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath != nil) { + [tableView selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition]; + } else { + NSLog(@"Failed to select row at index path %@ because the row never reached the view.", indexPath); + } + +} + +- (void)deselectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath != nil) { + [tableView deselectRowAtIndexPath:indexPath animated:animated]; + } else { + NSLog(@"Failed to deselect row at index path %@ because the row never reached the view.", indexPath); + } +} + +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + + if (indexPath != nil) { + [tableView scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; + } else { + NSLog(@"Failed to scroll to row at index path %@ because the row never reached the view.", indexPath); + } +} + #pragma mark - Querying Data +- (void)reloadDataInitiallyIfNeeded +{ + if (!self.dataController.initialReloadDataHasBeenCalled) { + [self reloadData]; + } +} + - (NSInteger)numberOfRowsInSection:(NSInteger)section { + [self reloadDataInitiallyIfNeeded]; return [self.dataController numberOfRowsInSection:section]; } - (NSInteger)numberOfSections { + [self reloadDataInitiallyIfNeeded]; return [self.dataController numberOfSections]; } +- (NSArray<__kindof ASCellNode *> *)visibleNodes +{ + ASDisplayNodeAssertMainThread(); + return self.isNodeLoaded ? [self.view visibleNodes] : @[]; +} + - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { return [self.dataController indexPathForNode:cellNode]; @@ -272,9 +418,75 @@ ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) - (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath { + [self reloadDataInitiallyIfNeeded]; return [self.dataController nodeAtIndexPath:indexPath]; } +- (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + return [tableView rectForRowAtIndexPath:indexPath]; +} + +- (nullable __kindof UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + indexPath = [tableView convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; + if (indexPath == nil) { + return nil; + } + return [tableView cellForRowAtIndexPath:indexPath]; +} + +- (nullable NSIndexPath *)indexPathForSelectedRow +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + NSIndexPath *indexPath = tableView.indexPathForSelectedRow; + if (indexPath != nil) { + return [tableView convertIndexPathToTableNode:indexPath]; + } + return indexPath; +} + +- (nullable NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + + NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:point]; + if (indexPath != nil) { + return [tableView convertIndexPathToTableNode:indexPath]; + } + return indexPath; +} + +- (nullable NSArray *)indexPathsForRowsInRect:(CGRect)rect +{ + ASDisplayNodeAssertMainThread(); + ASTableView *tableView = self.view; + return [tableView convertIndexPathsToTableNode:[tableView indexPathsForRowsInRect:rect]]; +} + +- (NSArray *)indexPathsForVisibleRows +{ + ASDisplayNodeAssertMainThread(); + NSMutableArray *indexPathsArray = [NSMutableArray new]; + for (ASCellNode *cell in [self visibleNodes]) { + NSIndexPath *indexPath = [self indexPathForNode:cell]; + if (indexPath) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; +} + #pragma mark - Editing - (void)reloadDataWithCompletion:(void (^)())completion @@ -287,6 +499,11 @@ ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock) [self reloadDataWithCompletion:nil]; } +- (void)relayoutItems +{ + [self.view relayoutItems]; +} + - (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion { [self.view beginUpdates]; diff --git a/AsyncDisplayKit/ASTableView.h b/AsyncDisplayKit/ASTableView.h index 8876409753..b454e497ed 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/AsyncDisplayKit/ASTableView.h @@ -74,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN * * @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants. */ -- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style ASDISPLAYNODE_DEPRECATED; +- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style ASDISPLAYNODE_DEPRECATED_MSG("Please use ASTableNode instead of ASTableView."); /** * Tuning parameters for a range type in full mode. @@ -86,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Set the tuning parameters for a range type in full mode. @@ -97,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Tuning parameters for a range type in the specified mode. @@ -110,7 +110,7 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Set the tuning parameters for a range type in the specified mode. @@ -122,14 +122,25 @@ NS_ASSUME_NONNULL_BEGIN * @see ASLayoutRangeMode * @see ASLayoutRangeType */ -- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED; +- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +/** + * Scrolls the table to the given row. + * + * @param indexPath The index path of the row. + * @param scrollPosition Where the row should end up after the scroll. + * @param animated Whether the scroll should be animated or not. + */ +- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); + +- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition; ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Similar to -visibleCells. * * @return an array containing the cell nodes being displayed on screen. */ -- (NSArray *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (NSArray *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Similar to -indexPathForCell:. @@ -138,7 +149,7 @@ NS_ASSUME_NONNULL_BEGIN * * @return an indexPath for this cellNode */ -- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Reload everything from scratch, destroying the working range and all cached nodes. @@ -147,14 +158,14 @@ NS_ASSUME_NONNULL_BEGIN * the main thread. * @warning This method is substantially more expensive than UITableView's version. */ --(void)reloadDataWithCompletion:(void (^ _Nullable)())completion ASDISPLAYNODE_DEPRECATED; +-(void)reloadDataWithCompletion:(void (^ _Nullable)())completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Reload everything from scratch, destroying the working range and all cached nodes. * * @warning This method is substantially more expensive than UITableView's version. */ -- (void)reloadData ASDISPLAYNODE_DEPRECATED; +- (void)reloadData ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes. @@ -162,14 +173,14 @@ NS_ASSUME_NONNULL_BEGIN * @warning This method is substantially more expensive than UITableView's version and will block the main thread while * all the cells load. */ -- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED; +- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's reloadDataWithCompletion: followed by ASTableNode's -waitUntilAllUpdatesAreCommitted instead."); /** * Triggers a relayout of all nodes. * * @discussion This method invalidates and lays out every cell node in the table view. */ -- (void)relayoutItems ASDISPLAYNODE_DEPRECATED; +- (void)relayoutItems ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Begins 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. @@ -180,7 +191,7 @@ NS_ASSUME_NONNULL_BEGIN * * @warning This method must be called from the main thread. */ -- (void)beginUpdates ASDISPLAYNODE_DEPRECATED; +- (void)beginUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); /** * 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. @@ -191,7 +202,7 @@ NS_ASSUME_NONNULL_BEGIN * * @warning This method is must be called from the main thread. */ -- (void)endUpdates ASDISPLAYNODE_DEPRECATED; +- (void)endUpdates ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); /** * Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view. @@ -206,12 +217,12 @@ NS_ASSUME_NONNULL_BEGIN * 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 (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED; +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's -performBatchUpdates:completion: instead."); /** * Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread. */ -- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED; +- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Inserts one or more sections, with an option to animate the insertion. @@ -223,7 +234,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Deletes one or more sections, with an option to animate the deletion. @@ -235,7 +246,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Reloads the specified sections using a given animation effect. @@ -247,7 +258,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Moves a section to a new location. @@ -259,7 +270,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion. @@ -271,7 +282,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Deletes the rows specified by an array of index paths, with an option to animate the deletion. @@ -283,7 +294,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Reloads the specified rows using a given animation effect. @@ -295,7 +306,7 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /** * Moves the row at a specified location to a destination location. @@ -307,21 +318,21 @@ NS_ASSUME_NONNULL_BEGIN * @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 ASDISPLAYNODE_DEPRECATED; +- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode method instead."); /// Deprecated in 2.0. You should not call this method. -- (void)clearContents ASDISPLAYNODE_DEPRECATED; +- (void)clearContents ASDISPLAYNODE_DEPRECATED_MSG("You should not call this method directly. Intead, rely on the Interstate State callback methods."); /// Deprecated in 2.0. You should not call this method. -- (void)clearFetchedData ASDISPLAYNODE_DEPRECATED; +- (void)clearFetchedData ASDISPLAYNODE_DEPRECATED_MSG("You should not call this method directly. Intead, rely on the Interstate State callback methods."); @end -ASDISPLAYNODE_DEPRECATED +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASTableDataSource.") @protocol ASTableViewDataSource @end -ASDISPLAYNODE_DEPRECATED +ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASTableDelegate.") @protocol ASTableViewDelegate @end diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 55848f3298..8a3334eac5 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -352,10 +352,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)]; _asyncDelegateFlags.tableViewWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDisplayRowWithNode:)]; if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow == NO) { _asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)]; } _asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)]; + _asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didEndDisplayingRowWithNode:)]; _asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]; _asyncDelegateFlags.tableViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)]; _asyncDelegateFlags.tableNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableNode:willBeginBatchFetchWithContext:)]; @@ -462,14 +464,43 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - return [self indexPathForNode:node waitingIfNeeded:wait]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.row == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + return [self indexPathForNode:node waitingIfNeeded:wait]; + } } - (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath { - ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + // If this is a section index path, we don't currently have a method + // to do a mapping. + if (indexPath.row == NSNotFound) { + return indexPath; + } else { + ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; + return [_dataController indexPathForNode:node]; + } +} + +- (NSArray *)convertIndexPathsToTableNode:(NSArray *)indexPaths +{ + if (indexPaths == nil) { + return nil; + } + + NSMutableArray *indexPathsArray = [NSMutableArray new]; + + for (NSIndexPath *indexPathInView in indexPaths) { + NSIndexPath *indexPath = [self convertIndexPathToTableNode:indexPathInView]; + if (indexPath != nil) { + [indexPathsArray addObject:indexPath]; + } + } + return indexPathsArray; } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode @@ -526,42 +557,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_dataController waitUntilAllUpdatesAreCommitted]; } -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - * When the behavior is changed (to use the view index path directly) - * we should also remove the @c convertIndexPathFromTableNode: method. - */ -- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated -{ - ASDisplayNodeAssertMainThread(); - - indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; - if (indexPath != nil) { - [super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated]; - } else { - NSLog(@"Warning: Ignoring request to scroll to row at index path %@ because the item did not reach the table view.", indexPath); - } -} - -/** - * TODO: This method was built when the distinction between data source - * index paths and view index paths was unclear. For compatibility, it - * still expects data source index paths for the time being. - */ -- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition -{ - ASDisplayNodeAssertMainThread(); - - indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES]; - if (indexPath != nil) { - [super selectRowAtIndexPath:indexPath animated:YES scrollPosition:scrollPosition]; - } else { - NSLog(@"Warning: Ignoring request to select row at index path %@ because the item did not reach the table view.", indexPath); - } -} - - (void)layoutSubviews { if (_nodesConstrainedWidth != self.bounds.size.width) { diff --git a/AsyncDisplayKit/ASTableViewInternal.h b/AsyncDisplayKit/ASTableViewInternal.h index d8f4afdcf2..8b36014a52 100644 --- a/AsyncDisplayKit/ASTableViewInternal.h +++ b/AsyncDisplayKit/ASTableViewInternal.h @@ -37,4 +37,26 @@ /// Set YES and we'll log every time we call [super insertRows…] etc @property (nonatomic) BOOL test_enableSuperUpdateCallLogging; +/** + * Attempt to get the view-layer index path for the row with the given index path. + * + * @param indexPath The index path of the row. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + +/** + * Attempt to get the node index path given the view-layer index path. + * + * @param indexPath The index path of the row. + */ +- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath; + +/** + * Attempt to get the node index paths given the view-layer index paths. + * + * @param indexPaths An array of index paths in the view space + */ +- (NSArray *)convertIndexPathsToTableNode:(NSArray *)indexPaths; + @end diff --git a/AsyncDisplayKit/ASTableViewProtocols.h b/AsyncDisplayKit/ASTableViewProtocols.h index 9d2e220c65..00719dea26 100644 --- a/AsyncDisplayKit/ASTableViewProtocols.h +++ b/AsyncDisplayKit/ASTableViewProtocols.h @@ -19,9 +19,9 @@ NS_ASSUME_NONNULL_BEGIN @optional -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED; +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:numberOfRowsInSection: instead."); -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView ASDISPLAYNODE_DEPRECATED; +- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView ASDISPLAYNODE_DEPRECATED_MSG("Implement numberOfSectionsInTableNode: instead."); - (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section; - (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; @@ -62,14 +62,14 @@ NS_ASSUME_NONNULL_BEGIN - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath; -- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:shouldHighlightRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didHighlightRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didUnhighlightRowAtIndexPath: instead."); -- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:willSelectRowAtIndexPath: instead."); +- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:willDeselectRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didSelectRowAtIndexPath: instead."); +- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:didDeselectRowAtIndexPath: instead."); - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; - (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath; @@ -85,9 +85,9 @@ NS_ASSUME_NONNULL_BEGIN - (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath; -- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED; -- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED; -- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED; +- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:shouldShowMenuForRowAtIndexPath: instead."); +- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:canPerformAction:forRowAtIndexPath:withSender: instead."); +- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED_MSG("Implement -tableNode:performAction:forRowAtIndexPath:withSender: instead."); @end diff --git a/AsyncDisplayKit/ASTextNode.h b/AsyncDisplayKit/ASTextNode.h index 354039e238..78bdd12005 100644 --- a/AsyncDisplayKit/ASTextNode.h +++ b/AsyncDisplayKit/ASTextNode.h @@ -299,7 +299,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @see attributedText */ -@property (nullable, nonatomic, copy) NSAttributedString *attributedString ASDISPLAYNODE_DEPRECATED; +@property (nullable, nonatomic, copy) NSAttributedString *attributedString ASDISPLAYNODE_DEPRECATED_MSG("Use .attributedText instead."); /** @@ -308,8 +308,10 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) { @see truncationAttributedText */ -@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString ASDISPLAYNODE_DEPRECATED; +@property (nullable, nonatomic, copy) NSAttributedString *truncationAttributedString ASDISPLAYNODE_DEPRECATED_MSG("Use .truncationAttributedText instead."); @end NS_ASSUME_NONNULL_END + + diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index 3de201d87e..f287eb931f 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -1356,7 +1356,7 @@ static NSAttributedString *DefaultTruncationAttributedString() } // If we've reached this point, both _additionalTruncationMessage and - // _truncationAttributedString are present. Compose them. + // _truncationAttributedText are present. Compose them. NSMutableAttributedString *newComposedTruncationString = [[NSMutableAttributedString alloc] initWithAttributedString:_truncationAttributedText]; [newComposedTruncationString replaceCharactersInRange:NSMakeRange(newComposedTruncationString.length, 0) withString:@" "]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 8ffa75ca0c..444d8fc7aa 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -233,15 +233,14 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableContexts:(NSMutableArray *)contexts { - id environment = [self.environmentDelegate dataControllerEnvironment]; - ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection; - + __weak id environment = [self.environmentDelegate dataControllerEnvironment]; + [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { for (NSUInteger sec = range.location; sec < NSMaxRange(range); sec++) { NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec]; for (NSUInteger i = 0; i < itemCount; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec]; - [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection]; + [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environment:environment]; } } }]; @@ -249,8 +248,7 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths mutableContexts:(NSMutableArray *)contexts { - id environment = [self.environmentDelegate dataControllerEnvironment]; - ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection; + __weak id environment = [self.environmentDelegate dataControllerEnvironment]; NSMutableIndexSet *sections = [NSMutableIndexSet indexSet]; for (NSIndexPath *indexPath in indexPaths) { @@ -262,13 +260,13 @@ NSUInteger itemCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:sec]; for (NSUInteger i = 0; i < itemCount; i++) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec]; - [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection]; + [self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environment:environment]; } } }]; } -- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray *)contexts environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection +- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray *)contexts environment:(id)environment { ASCellNodeBlock supplementaryCellBlock; if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) { @@ -283,7 +281,7 @@ indexPath:indexPath supplementaryElementKind:kind constrainedSize:constrainedSize - environmentTraitCollection:environmentTraitCollection]; + environment:environment]; [contexts addObject:context]; } diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.h b/AsyncDisplayKit/Details/ASCollectionInternal.h index c76238ef70..c17c8d689a 100644 --- a/AsyncDisplayKit/Details/ASCollectionInternal.h +++ b/AsyncDisplayKit/Details/ASCollectionInternal.h @@ -25,6 +25,22 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode; @property (nonatomic, strong, readonly) ASDataController *dataController; @property (nonatomic, strong, readonly) ASRangeController *rangeController; + +/** + * Attempt to get the view-layer index path for the item with the given index path. + * + * @param indexPath The index path of the item. + * @param wait If the item hasn't reached the view yet, this attempts to wait for updates to commit. + */ +- (nullable NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait; + +/** + * Attempt to get the node index path given the view-layer index path. + * + * @param indexPath The index path of the row. + */ +- (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath; + @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index e0e560ecf8..3c34bfc909 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -66,7 +66,7 @@ NS_ASSUME_NONNULL_BEGIN * @deprecated This method will not be called, and it is only deprecated as a reminder to remove it. * Supplementary elements must exist in the same sections as regular collection view items i.e. -numberOfSectionsInCollectionView: */ -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind ASDISPLAYNODE_DEPRECATED; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode's method instead."); @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index ef9b35a7ca..504d0d5f2b 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -34,6 +34,7 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView @implementation ASCollectionViewLayoutInspector { struct { + unsigned int implementsConstrainedSizeForNodeAtIndexPathDeprecated:1; unsigned int implementsConstrainedSizeForNodeAtIndexPath:1; } _delegateFlags; } @@ -56,14 +57,16 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView if (delegate == nil) { memset(&_delegateFlags, 0, sizeof(_delegateFlags)); } else { - _delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionNode:constrainedSizeForItemAtIndexPath:)]; } } - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath) { - // TODO: Handle collection node + return [collectionView.asyncDelegate collectionNode:collectionView.collectionNode constrainedSizeForItemAtIndexPath:indexPath]; + } else if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; @@ -101,7 +104,8 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView struct { unsigned int implementsReferenceSizeForHeader:1; unsigned int implementsReferenceSizeForFooter:1; - unsigned int implementsConstrainedSizeForNodeAtIndexPath:1; + unsigned int implementsConstrainedSizeForNodeAtIndexPathDeprecated:1; + unsigned int implementsConstrainedSizeForItemAtIndexPath:1; } _delegateFlags; struct { @@ -134,7 +138,8 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView } else { _delegateFlags.implementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; _delegateFlags.implementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; - _delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)]; + _delegateFlags.implementsConstrainedSizeForItemAtIndexPath = [delegate respondsToSelector:@selector(collectionNode:constrainedSizeForItemAtIndexPath:)]; } } @@ -149,7 +154,9 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath) { + if (_delegateFlags.implementsConstrainedSizeForItemAtIndexPath) { + return [collectionView.asyncDelegate collectionNode:collectionView.collectionNode constrainedSizeForItemAtIndexPath:indexPath]; + } else if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPathDeprecated) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" return [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index 6672fb7b2a..14d1a0021e 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -30,7 +30,6 @@ typedef struct ASRangeGeometry ASRangeGeometry; @package ASCollectionView * __weak _collectionView; UICollectionViewLayout * __strong _collectionViewLayout; - ASScrollDirection _scrollableDirections; } @end @@ -42,7 +41,6 @@ typedef struct ASRangeGeometry ASRangeGeometry; return nil; } - _scrollableDirections = [collectionView scrollableDirections]; _collectionView = collectionView; _collectionViewLayout = [collectionView collectionViewLayout]; return self; @@ -74,12 +72,7 @@ typedef struct ASRangeGeometry ASRangeGeometry; { CGRect rect = _collectionView.bounds; - // Scrollable directions can change for non-flow layouts - if ([_collectionViewLayout asdk_isFlowLayout] == NO) { - _scrollableDirections = [_collectionView scrollableDirections]; - } - - return CGRectExpandToRangeWithScrollableDirections(rect, tuningParameters, _scrollableDirections, scrollDirection); + return CGRectExpandToRangeWithScrollableDirections(rect, tuningParameters, [_collectionView scrollableDirections], scrollDirection); } @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 16f2ab87b0..75be3cb8b9 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -23,8 +23,6 @@ #import "ASInternalHelpers.h" #import "ASCellNode+Internal.h" -#import - //#define LOG(...) NSLog(__VA_ARGS__) #define LOG(...) @@ -429,12 +427,16 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements - NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; - NSUInteger editingNodesSectionCount = editingNodes.count; - - if (editingNodesSectionCount) { - NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodesSectionCount)]; - [self _deleteNodesAtIndexPaths:ASIndexPathsForTwoDimensionalArray(editingNodes) withAnimationOptions:animationOptions]; + NSUInteger oldSectionCount = [_editingNodes[ASDataControllerRowNodeKind] count]; + + // If we have old sections, we should delete them inside beginUpdates/endUpdates with inserting the new ones. + if (oldSectionCount) { + // -beginUpdates + [_mainSerialQueue performBlockOnMainThread:^{ + [_delegate dataControllerBeginUpdates:self]; + }]; + + NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, oldSectionCount)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; } @@ -447,6 +449,13 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; } [self _insertSections:sections atIndexSet:sectionIndexes withAnimationOptions:animationOptions]; + if (oldSectionCount) { + // -endUpdates + [_mainSerialQueue performBlockOnMainThread:^{ + [_delegate dataController:self endUpdatesAnimated:NO completion:nil]; + }]; + } + [self _batchLayoutAndInsertNodesFromContexts:newContexts withAnimationOptions:animationOptions]; if (completion) { @@ -478,8 +487,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; { ASDisplayNodeAssertMainThread(); - id environment = [self.environmentDelegate dataControllerEnvironment]; - ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection; + __weak id environment = [self.environmentDelegate dataControllerEnvironment]; std::vector counts = [self itemCountsFromDataSource]; NSMutableArray *contexts = [NSMutableArray array]; @@ -495,7 +503,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; indexPath:indexPath supplementaryElementKind:nil constrainedSize:constrainedSize - environmentTraitCollection:environmentTraitCollection]]; + environment:environment]]; } } }]; @@ -626,9 +634,6 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; // remove elements LOG(@"Edit Transaction - deleteSections: %@", sections); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); - - [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteSectionsAtIndexSet:sections withAnimationOptions:animationOptions]; }); } @@ -745,8 +750,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; NSMutableArray *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; - id environment = [self.environmentDelegate dataControllerEnvironment]; - ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection; + __weak id environment = [self.environmentDelegate dataControllerEnvironment]; for (NSIndexPath *indexPath in sortedIndexPaths) { ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath]; @@ -755,7 +759,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; indexPath:indexPath supplementaryElementKind:nil constrainedSize:constrainedSize - environmentTraitCollection:environmentTraitCollection]]; + environment:environment]]; } ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodeContexts[ASDataControllerRowNodeKind], sortedIndexPaths, contexts); diff --git a/AsyncDisplayKit/Details/ASDelegateProxy.m b/AsyncDisplayKit/Details/ASDelegateProxy.m index 460c50f0b6..38c32d2f49 100644 --- a/AsyncDisplayKit/Details/ASDelegateProxy.m +++ b/AsyncDisplayKit/Details/ASDelegateProxy.m @@ -116,10 +116,10 @@ { return ( // handled by ASPagerDataSource node<->cell machinery - selector == @selector(collectionView:nodeForItemAtIndexPath:) || - selector == @selector(collectionView:nodeBlockForItemAtIndexPath:) || - selector == @selector(collectionView:numberOfItemsInSection:) || - selector == @selector(collectionView:constrainedSizeForNodeAtIndexPath:) + selector == @selector(collectionNode:nodeForItemAtIndexPath:) || + selector == @selector(collectionNode:nodeBlockForItemAtIndexPath:) || + selector == @selector(collectionNode:numberOfItemsInSection:) || + selector == @selector(collectionNode:constrainedSizeForItemAtIndexPath:) ); } diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index 4fcb27f356..e8f2bf6159 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -110,8 +110,9 @@ ASDISPLAYNODE_EXTERN_C_END /// Returns all children of an object which class conforms to the ASEnvironment protocol - (nullable NSArray> *)children; -/// Classes should implement this method and return YES / NO dependent if upward propagation is enabled or not -- (BOOL)supportsUpwardPropagation; +/// Classes should implement this method and return YES / NO dependent if upward propagation is enabled or not +// Currently this is disabled as propagation of any attributions besides trait collections is not supported at the moment +// - (BOOL)supportsUpwardPropagation; /// Classes should implement this method and return YES / NO dependent if downware propagation is enabled or not - (BOOL)supportsTraitsCollectionPropagation; diff --git a/AsyncDisplayKit/Details/ASIndexPath.m b/AsyncDisplayKit/Details/ASIndexPath.m index 8c2234e639..bca083d5fc 100644 --- a/AsyncDisplayKit/Details/ASIndexPath.m +++ b/AsyncDisplayKit/Details/ASIndexPath.m @@ -64,7 +64,7 @@ BOOL ASIndexPathRangeEqualToIndexPathRange(ASIndexPathRange first, ASIndexPathRa + (NSIndexPath *)indexPathWithASIndexPath:(ASIndexPath)indexPath { - return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];; + return [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section]; } - (ASIndexPath)ASIndexPathValue diff --git a/AsyncDisplayKit/Details/ASIndexedNodeContext.h b/AsyncDisplayKit/Details/ASIndexedNodeContext.h index 6e7992135d..2bf7822221 100644 --- a/AsyncDisplayKit/Details/ASIndexedNodeContext.h +++ b/AsyncDisplayKit/Details/ASIndexedNodeContext.h @@ -24,13 +24,14 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, strong) NSIndexPath *indexPath; @property (nonatomic, readonly, copy, nullable) NSString *supplementaryElementKind; @property (nonatomic, readonly, assign) ASSizeRange constrainedSize; +@property (weak, nonatomic) id environment; @property (nonatomic, readonly, assign) ASEnvironmentTraitCollection environmentTraitCollection; - (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock indexPath:(NSIndexPath *)indexPath supplementaryElementKind:(nullable NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize - environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection; + environment:(id)environment; /** * @return The node, running the node block if necessary. The node block will be discarded diff --git a/AsyncDisplayKit/Details/ASIndexedNodeContext.mm b/AsyncDisplayKit/Details/ASIndexedNodeContext.mm index 38624e3a42..15020eecb1 100644 --- a/AsyncDisplayKit/Details/ASIndexedNodeContext.mm +++ b/AsyncDisplayKit/Details/ASIndexedNodeContext.mm @@ -31,7 +31,7 @@ indexPath:(NSIndexPath *)indexPath supplementaryElementKind:(nullable NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize - environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection + environment:(id)environment { NSAssert(nodeBlock != nil && indexPath != nil, @"Node block and index path must not be nil"); self = [super init]; @@ -40,7 +40,8 @@ _indexPath = indexPath; _supplementaryElementKind = [supplementaryElementKind copy]; _constrainedSize = constrainedSize; - _environmentTraitCollection = environmentTraitCollection; + _environment = environment; + _environmentTraitCollection = environment.environmentTraitCollection; } return self; } @@ -57,6 +58,7 @@ } node.cachedIndexPath = _indexPath; node.supplementaryElementKind = _supplementaryElementKind; + node.owningNode = (ASDisplayNode *)_environment; ASEnvironmentStatePropagateDown(node, _environmentTraitCollection); _node = node; } diff --git a/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m b/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m index 241fb71810..6ae4a0437d 100644 --- a/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m +++ b/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m @@ -27,7 +27,7 @@ #import #import -#import +#import #if PIN_ANIMATED_AVAILABLE @@ -75,7 +75,7 @@ @implementation ASPINRemoteImageManager //Share image cache with sharedImageManager image cache. -- (PINCache *)defaultImageCache +- (id )defaultImageCache { return [[PINRemoteImageManager sharedImageManager] cache]; } @@ -124,6 +124,16 @@ return sharedPINRemoteImageManager; } +- (BOOL)sharedImageManagerSupportsMemoryRemoval +{ + static BOOL sharedImageManagerSupportsMemoryRemoval = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedImageManagerSupportsMemoryRemoval = [[[self sharedPINRemoteImageManager] cache] respondsToSelector:@selector(removeObjectForKeyFromMemory:)]; + }); + return sharedImageManagerSupportsMemoryRemoval; +} + #pragma mark ASImageProtocols #if PIN_ANIMATED_AVAILABLE @@ -163,9 +173,11 @@ - (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL { - PINRemoteImageManager *manager = [self sharedPINRemoteImageManager]; - NSString *key = [manager cacheKeyForURL:URL processorKey:nil]; - [[[manager cache] memoryCache] removeObjectForKey:key]; + if ([self sharedImageManagerSupportsMemoryRemoval]) { + PINRemoteImageManager *manager = [self sharedPINRemoteImageManager]; + NSString *key = [manager cacheKeyForURL:URL processorKey:nil]; + [[manager cache] removeObjectForKeyFromMemory:key]; + } } - (nullable id)downloadImageWithURL:(NSURL *)URL diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index ab4dfc7e3e..2da5de760a 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -22,8 +22,6 @@ #import "ASDisplayNode+FrameworkPrivate.h" #import "AsyncDisplayKit+Debug.h" -#import - #define AS_RANGECONTROLLER_LOG_UPDATE_FREQ 0 @interface ASRangeController () diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm b/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm index 82ddb2a686..55824942ce 100644 --- a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm +++ b/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm @@ -36,7 +36,7 @@ NSInteger const ASDefaultTransactionPriority = 0; - (instancetype)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock { if ((self = [super init])) { - _operationCompletionBlock = [operationCompletionBlock copy]; + _operationCompletionBlock = operationCompletionBlock; } return self; } @@ -339,7 +339,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance() callbackQueue = dispatch_get_main_queue(); } _callbackQueue = callbackQueue; - _completionBlock = [completionBlock copy]; + _completionBlock = completionBlock; _state = ATOMIC_VAR_INIT(ASAsyncTransactionStateOpen); } diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h b/AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h index 58c5973ed6..7b6d269259 100644 --- a/AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h +++ b/AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h @@ -20,6 +20,11 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign) CGPoint layoutPosition; + +#pragma mark Deprecated + +@property (nonatomic, assign) ASRelativeSizeRange sizeRange ASDISPLAYNODE_DEPRECATED; + @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h index a2b46d921b..0f3cc89522 100644 --- a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h @@ -10,15 +10,32 @@ #import +/** How much space the spec will take up. */ +typedef NS_ENUM(NSInteger, ASAbsoluteLayoutSpecSizing) { + /** The spec will take up the maximum size possible. */ + ASAbsoluteLayoutSpecSizingDefault, + /** Computes a size for the spec that is the union of all childrens' frames. */ + ASAbsoluteLayoutSpecSizingSizeToFit, +}; + NS_ASSUME_NONNULL_BEGIN /** - * 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. */ @interface ASAbsoluteLayoutSpec : ASLayoutSpec +/** + How much space will the spec taken up + */ +@property (nonatomic, assign) ASAbsoluteLayoutSpecSizing sizing; + +/** + @param sizing How much space the spec will take up + @param children Children to be positioned at fixed positions + */ ++ (instancetype)absoluteLayoutSpecWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children AS_WARN_UNUSED_RESULT; + /** @param children Children to be positioned at fixed positions */ @@ -26,4 +43,13 @@ NS_ASSUME_NONNULL_BEGIN @end + +#pragma mark - Deprecated + +@interface ASStaticLayoutSpec : ASAbsoluteLayoutSpec + ++ (instancetype)staticLayoutSpecWithChildren:(NSArray> *)children AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; + +@end + NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm index 64ff4e2d31..c4506ad72d 100644 --- a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm @@ -10,37 +10,58 @@ #import "ASAbsoluteLayoutSpec.h" -#import "ASLayoutSpecUtilities.h" #import "ASLayout.h" +#import "ASLayoutSpec+Subclasses.h" +#import "ASLayoutSpecUtilities.h" #import "ASLayoutElementStylePrivate.h" +#pragma mark - ASAbsoluteLayoutSpec + @implementation ASAbsoluteLayoutSpec +#pragma mark - Class + + (instancetype)absoluteLayoutSpecWithChildren:(NSArray *)children { return [[self alloc] initWithChildren:children]; } ++ (instancetype)absoluteLayoutSpecWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children +{ + return [[self alloc] initWithSizing:sizing children:children]; +} + +#pragma mark - Lifecycle + - (instancetype)init { - return [self initWithChildren:@[]]; + return [self initWithChildren:nil]; } - (instancetype)initWithChildren:(NSArray *)children +{ + return [self initWithSizing:ASAbsoluteLayoutSpecSizingDefault children:children]; +} + +- (instancetype)initWithSizing:(ASAbsoluteLayoutSpecSizing)sizing children:(NSArray> *)children { if (!(self = [super init])) { return nil; } + + _sizing = sizing; self.children = children; + return self; } +#pragma mark - ASLayoutSpec + - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { - // TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used CGSize size = { - (isinf(constrainedSize.max.width) || !ASPointsValidForLayout(constrainedSize.max.width)) ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, - (isinf(constrainedSize.max.height) || !ASPointsValidForLayout(constrainedSize.max.height)) ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height + ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, + ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height }; NSArray *children = self.children; @@ -60,14 +81,14 @@ [sublayouts addObject:sublayout]; } - if (isnan(size.width)) { + if (_sizing == ASAbsoluteLayoutSpecSizingSizeToFit || isnan(size.width)) { 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 (_sizing == ASAbsoluteLayoutSpecSizingSizeToFit || isnan(size.height)) { size.height = constrainedSize.min.height; for (ASLayout *sublayout in sublayouts) { size.height = MAX(size.height, sublayout.position.y + sublayout.size.height); @@ -79,22 +100,31 @@ @end -@implementation ASAbsoluteLayoutSpec (ASEnvironment) -- (BOOL)supportsUpwardPropagation -{ - return NO; -} - -@end +#pragma mark - Debugging @implementation ASAbsoluteLayoutSpec (Debugging) -#pragma mark - ASLayoutElementAsciiArtProtocol - - (NSString *)debugBoxString { return [ASLayoutSpec asciiArtStringForChildren:self.children parentName:[self asciiArtName]]; } @end + + +#pragma mark - ASStaticLayoutSpec + +@implementation ASStaticLayoutSpec : ASAbsoluteLayoutSpec + ++ (instancetype)staticLayoutSpecWithChildren:(NSArray> *)children +{ + return [self absoluteLayoutSpecWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit children:children]; +} + +- (instancetype)initWithChildren:(NSArray *)children +{ + return [super initWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit children:children]; +} + +@end diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index 9271d70d70..a94c051a6b 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -10,9 +10,7 @@ #import "ASBackgroundLayoutSpec.h" #import "ASLayoutSpec+Subclasses.h" - #import "ASAssert.h" -#import "ASLayout.h" static NSUInteger const kForegroundChildIndex = 0; static NSUInteger const kBackgroundChildIndex = 1; diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h index 12629e9e1c..a1474c4840 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h @@ -49,9 +49,7 @@ NS_ASSUME_NONNULL_BEGIN * Initializer. * * @param centeringOptions How the child is centered. - * * @param sizingOptions How much space will be taken up. - * * @param child The child to center. */ + (instancetype)centerLayoutSpecWithCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm index a1610671d6..cbd06bb8f9 100644 --- a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm @@ -58,21 +58,19 @@ - (ASRelativeLayoutSpecPosition)horizontalPositionFromCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions { - BOOL centerX = (centeringOptions & ASCenterLayoutSpecCenteringX) != 0; - if (centerX) { + if ((centeringOptions & ASCenterLayoutSpecCenteringX) != 0) { return ASRelativeLayoutSpecPositionCenter; } else { - return ASRelativeLayoutSpecPositionStart; + return ASRelativeLayoutSpecPositionNone; } } - (ASRelativeLayoutSpecPosition)verticalPositionFromCenteringOptions:(ASCenterLayoutSpecCenteringOptions)centeringOptions { - BOOL centerY = (centeringOptions & ASCenterLayoutSpecCenteringY) != 0; - if (centerY) { + if ((centeringOptions & ASCenterLayoutSpecCenteringY) != 0) { return ASRelativeLayoutSpecPositionCenter; } else { - return ASRelativeLayoutSpecPositionStart; + return ASRelativeLayoutSpecPositionNone; } } diff --git a/AsyncDisplayKit/Layout/ASDimension.h b/AsyncDisplayKit/Layout/ASDimension.h index 675e62a264..c59c8fb6fb 100644 --- a/AsyncDisplayKit/Layout/ASDimension.h +++ b/AsyncDisplayKit/Layout/ASDimension.h @@ -102,7 +102,7 @@ ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit if (unit == ASDimensionUnitAuto ) { ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0."); } else if (unit == ASDimensionUnitPoints) { - ASDisplayNodeCAssertPositiveReal(@"Points", value); + ASDisplayNodeCAssert(ASPointsValidForSize(value), @"ASDimension points value (%f) must be valid for size.", value); } else if (unit == ASDimensionUnitFraction) { ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", value); } @@ -132,7 +132,7 @@ ASOVERLOADABLE AS_WARN_UNUSED_RESULT extern ASDimension ASDimensionMake(NSString */ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMakeWithPoints(CGFloat points) { - ASDisplayNodeCAssertPositiveReal(@"Points", points); + ASDisplayNodeCAssert(ASPointsValidForSize(points), @"ASDimension points value (%f) must be valid for size.", points); return ASDimensionMake(ASDimensionUnitPoints, points); } @@ -192,6 +192,8 @@ typedef struct { ASDimension height; } ASLayoutSize; +extern ASLayoutSize const ASLayoutSizeAuto; + /* * Creates an ASLayoutSize with provided min and max dimensions. */ @@ -333,10 +335,81 @@ ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolv #pragma mark - Deprecated +/** + * A dimension relative to constraints to be provided in the future. + * A ASDimension can be one of three types: + * + * "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. + * + * "Points" - Just a number. It will always resolve to exactly this amount. + * + * "Percent" - Multiplied to a provided parent amount to resolve a final amount. + */ +typedef NS_ENUM(NSInteger, ASRelativeDimensionType) { + /** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */ + ASRelativeDimensionTypeAuto, + /** 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. */ + ASRelativeDimensionTypeFraction, +}; + +#define ASRelativeDimension ASDimension +#define ASRelativeSize ASLayoutSize +#define ASRelativeDimensionMakeWithPoints ASDimensionMakeWithPoints +#define ASRelativeDimensionMakeWithFraction ASDimensionMakeWithFraction + /** * Function is deprecated. Use ASSizeRangeMakeWithExactCGSize instead. */ -extern AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED; +extern AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED_MSG("Use ASSizeRangeMakeWithExactCGSize instead."); + +/** + Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout. + Used by ASStaticLayoutSpec. + */ +typedef struct { + ASLayoutSize min; + ASLayoutSize max; +} ASRelativeSizeRange; + +extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained; + +#pragma mark - ASRelativeDimension + +extern ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value) ASDISPLAYNODE_DEPRECATED; + +#pragma mark - ASRelativeSize + +extern ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height) ASDISPLAYNODE_DEPRECATED; + +/** Convenience constructor to provide size in points. */ +extern ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size) ASDISPLAYNODE_DEPRECATED; + +/** Convenience constructor to provide size as a fraction. */ +extern ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED; + +extern BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs) ASDISPLAYNODE_DEPRECATED; + +extern NSString *NSStringFromASRelativeSize(ASLayoutSize size) ASDISPLAYNODE_DEPRECATED; + +#pragma mark - ASRelativeSizeRange + +extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max) ASDISPLAYNODE_DEPRECATED; + +#pragma mark Convenience constructors to provide an exact size (min == max). +extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact) ASDISPLAYNODE_DEPRECATED; + +extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact) ASDISPLAYNODE_DEPRECATED; + +extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction) ASDISPLAYNODE_DEPRECATED; + +extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight) ASDISPLAYNODE_DEPRECATED; + +extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs) ASDISPLAYNODE_DEPRECATED; + +/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */ +extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize) ASDISPLAYNODE_DEPRECATED; NS_ASSUME_NONNULL_END ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKit/Layout/ASDimension.mm b/AsyncDisplayKit/Layout/ASDimension.mm index 260b232e51..c2cf83998b 100644 --- a/AsyncDisplayKit/Layout/ASDimension.mm +++ b/AsyncDisplayKit/Layout/ASDimension.mm @@ -72,6 +72,8 @@ NSString *NSStringFromASDimension(ASDimension dimension) #pragma mark - ASLayoutSize +ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; + // ** Resolve this relative size relative to a parent size. */ ASDISPLAYNODE_INLINE CGSize ASLayoutSizeResolveSize(ASLayoutSize layoutSize, CGSize parentSize, CGSize autoSize) { @@ -188,7 +190,86 @@ NSString *NSStringFromASSizeRange(ASSizeRange sizeRange) #pragma mark - Deprecated +ASDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value) +{ + if (type == ASRelativeDimensionTypePoints) { + return ASDimensionMakeWithPoints(value); + } else if (type == ASRelativeDimensionTypeFraction) { + return ASDimensionMakeWithFraction(value); + } + + ASDisplayNodeCAssert(NO, @"ASRelativeDimensionMake does not support the given ASRelativeDimensionType"); + return ASDimensionMakeWithPoints(0); +} + ASSizeRange ASSizeRangeMakeExactSize(CGSize size) { return ASSizeRangeMake(size); } + +ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {}; + +#pragma mark - ASRelativeSize + +ASLayoutSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height) +{ + return ASLayoutSizeMake(width, height); +} + +ASLayoutSize ASRelativeSizeMakeWithCGSize(CGSize size) +{ + return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width), + ASRelativeDimensionMakeWithPoints(size.height)); +} + +ASLayoutSize ASRelativeSizeMakeWithFraction(CGFloat fraction) +{ + return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction), + ASRelativeDimensionMakeWithFraction(fraction)); +} + +BOOL ASRelativeSizeEqualToRelativeSize(ASLayoutSize lhs, ASLayoutSize rhs) +{ + return ASDimensionEqualToDimension(lhs.width, rhs.width) + && ASDimensionEqualToDimension(lhs.height, rhs.height); +} + + +#pragma mark - ASRelativeSizeRange + +ASRelativeSizeRange ASRelativeSizeRangeMake(ASLayoutSize min, ASLayoutSize max) +{ + ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange; +} + +ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASLayoutSize exact) +{ + return ASRelativeSizeRangeMake(exact, exact); +} + +ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact) +{ + return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact)); +} + +ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction) +{ + return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction)); +} + +ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth, ASRelativeDimension exactHeight) +{ + return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight)); +} + +BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs) +{ + return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max); +} + +ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, + CGSize parentSize) +{ + return ASSizeRangeMake(ASLayoutSizeResolveSize(relativeSizeRange.min, parentSize, parentSize), + ASLayoutSizeResolveSize(relativeSizeRange.max, parentSize, parentSize)); +} diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 986d2e4002..156975a17d 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -9,11 +9,9 @@ // #import "ASInsetLayoutSpec.h" - #import "ASAssert.h" - +#import "ASLayoutSpec+Subclasses.h" #import "ASInternalHelpers.h" -#import "ASLayout.h" @interface ASInsetLayoutSpec () { diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 1c3c1af1e3..76389398f7 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -65,7 +65,7 @@ ASDISPLAYNODE_EXTERN_C_END * * @discussion When being used as a sublayout, this property must not equal CGPointNull. */ -@property (nonatomic, assign, readwrite) CGPoint position; +@property (nonatomic, assign, readonly) CGPoint position; /** * Array of ASLayouts. Each must have a valid non-null position. @@ -144,6 +144,23 @@ ASDISPLAYNODE_EXTERN_C_END @end +#pragma mark - Deprecated + +@interface ASLayout (Deprecated) + +- (id )layoutableObject ASDISPLAYNODE_DEPRECATED; + ++ (instancetype)layoutWithLayoutableObject:(id)layoutElement + constrainedSizeRange:(ASSizeRange)constrainedSizeRange + size:(CGSize)size ASDISPLAYNODE_DEPRECATED; + ++ (instancetype)layoutWithLayoutableObject:(id)layoutElement + constrainedSizeRange:(ASSizeRange)constrainedSizeRange + size:(CGSize)size + sublayouts:(nullable NSArray *)sublayouts AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED; + +@end + #pragma mark - Debugging @interface ASLayout (Debugging) diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 40633813e0..b9967f6199 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -8,11 +8,10 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import "ASLayout.h" - #import "ASDimension.h" #import "ASInternalHelpers.h" #import "ASLayoutSpecUtilities.h" +#import "ASLayoutSpec+Subclasses.h" #import #import "ASObjectDescriptionHelpers.h" @@ -73,7 +72,7 @@ static inline NSString * descriptionIndents(NSUInteger indents) // Read this now to avoid @c weak overhead later. _layoutElementType = layoutElement.layoutElementType; - if (!ASIsCGSizeValidForLayout(size)) { + if (!ASIsCGSizeValidForSize(size)) { ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutElement); size = CGSizeZero; } else { @@ -243,6 +242,30 @@ static inline NSString * descriptionIndents(NSUInteger indents) @end +@implementation ASLayout (Deprecation) + +- (id )layoutableObject +{ + return self.layoutElement; +} + ++ (instancetype)layoutWithLayoutableObject:(id)layoutElement + constrainedSizeRange:(ASSizeRange)constrainedSizeRange + size:(CGSize)size +{ + return [self layoutWithLayoutElement:layoutElement size:size]; +} + ++ (instancetype)layoutWithLayoutableObject:(id)layoutElement + constrainedSizeRange:(ASSizeRange)constrainedSizeRange + size:(CGSize)size + sublayouts:(nullable NSArray *)sublayouts +{ + return [self layoutWithLayoutElement:layoutElement size:size sublayouts:sublayouts]; +} + +@end + ASLayout *ASCalculateLayout(id layoutElement, const ASSizeRange sizeRange, const CGSize parentSize) { ASDisplayNodeCAssertNotNil(layoutElement, @"Not valid layoutElement passed in."); diff --git a/AsyncDisplayKit/Layout/ASLayoutElement.h b/AsyncDisplayKit/Layout/ASLayoutElement.h index e933ee36e3..1e7b4c3967 100644 --- a/AsyncDisplayKit/Layout/ASLayoutElement.h +++ b/AsyncDisplayKit/Layout/ASLayoutElement.h @@ -50,7 +50,7 @@ NS_ASSUME_NONNULL_BEGIN * access to the options via convenience properties. If you are creating custom layout spec, then you can * extend the backing layout options class to accommodate any new layout options. */ -@protocol ASLayoutElement +@protocol ASLayoutElement #pragma mark - Getter @@ -69,6 +69,10 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign, readonly) ASLayoutElementStyle *style; +/** + * @abstract Optional name that is printed by ascii art string and displayed in description. + */ +@property (nullable, nonatomic, copy) NSString *debugName; #pragma mark - Calculate layout @@ -145,9 +149,9 @@ NS_ASSUME_NONNULL_BEGIN * * @return An ASLayout instance defining the layout of the receiver and its children. * - * @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead + * @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout() or ASCalculateLayout() instead */ -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED; +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED_MSG("Use layoutThatFits: instead"); @end @@ -250,10 +254,11 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty; * @discussion This method is optional, but one of either preferredSize or preferredLayoutSize is required * for nodes that either have 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. + * set on an ASImageNode to display at a size different from the underlying image size. + * + * @warning Calling the getter when the size's width or height are relative will cause an assert. */ @property (nonatomic, assign) CGSize preferredSize; -- (CGSize)preferredSize UNAVAILABLE_ATTRIBUTE; /** * @abstract An optional property that provides a minimum size bound for a layout element. If provided, this restriction will @@ -314,3 +319,4 @@ extern NSString * const ASLayoutElementStyleLayoutPositionProperty; @end NS_ASSUME_NONNULL_END +#define ASLayoutable ASLayoutElement diff --git a/AsyncDisplayKit/Layout/ASLayoutElement.mm b/AsyncDisplayKit/Layout/ASLayoutElement.mm index 087ec1af57..cad4cee86b 100644 --- a/AsyncDisplayKit/Layout/ASLayoutElement.mm +++ b/AsyncDisplayKit/Layout/ASLayoutElement.mm @@ -13,6 +13,7 @@ #import "ASDisplayNodeInternal.h" #import +#import CGFloat const ASLayoutElementParentDimensionUndefined = NAN; CGSize const ASLayoutElementParentSizeUndefined = {ASLayoutElementParentDimensionUndefined, ASLayoutElementParentDimensionUndefined}; @@ -110,21 +111,21 @@ do {\ @implementation ASLayoutElementStyle { ASDN::RecursiveMutex __instanceLock__; ASLayoutElementSize _size; + + std::atomic _spacingBefore; + std::atomic _spacingAfter; + std::atomic _flexGrow; + std::atomic _flexShrink; + std::atomic _flexBasis; + std::atomic _alignSelf; + std::atomic _ascender; + std::atomic _descender; + std::atomic _layoutPosition; } @dynamic width, height, minWidth, maxWidth, minHeight, maxHeight; @dynamic preferredSize, minSize, maxSize, preferredLayoutSize, minLayoutSize, maxLayoutSize; -@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 layoutPosition = _layoutPosition; - #pragma mark - Lifecycle - (instancetype)initWithDelegate:(id)delegate @@ -242,22 +243,49 @@ do {\ #pragma mark - ASLayoutElementStyleSizeHelpers +// We explicitly not call the setter for (max/min) width and height to avoid locking overhead + - (void)setPreferredSize:(CGSize)preferredSize { - self.width = ASDimensionMakeWithPoints(preferredSize.width); - self.height = ASDimensionMakeWithPoints(preferredSize.height); + ASDN::MutexLocker l(__instanceLock__); + _size.width = ASDimensionMakeWithPoints(preferredSize.width); + _size.height = ASDimensionMakeWithPoints(preferredSize.height); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); +} + +- (CGSize)preferredSize +{ + ASDN::MutexLocker l(__instanceLock__); + if (_size.width.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional width. Width: %@.", NSStringFromASDimension(_size.width)); + return CGSizeZero; + } + + if (_size.height.unit == ASDimensionUnitFraction) { + NSCAssert(NO, @"Cannot get preferredSize of element with fractional height. Height: %@.", NSStringFromASDimension(_size.height)); + return CGSizeZero; + } + + return CGSizeMake(_size.width.value, _size.height.value); } - (void)setMinSize:(CGSize)minSize { - self.minWidth = ASDimensionMakeWithPoints(minSize.width); - self.minHeight = ASDimensionMakeWithPoints(minSize.height); + ASDN::MutexLocker l(__instanceLock__); + _size.minWidth = ASDimensionMakeWithPoints(minSize.width); + _size.minHeight = ASDimensionMakeWithPoints(minSize.height); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); } - (void)setMaxSize:(CGSize)maxSize { - self.maxWidth = ASDimensionMakeWithPoints(maxSize.width); - self.maxHeight = ASDimensionMakeWithPoints(maxSize.height); + ASDN::MutexLocker l(__instanceLock__); + _size.maxWidth = ASDimensionMakeWithPoints(maxSize.width); + _size.maxHeight = ASDimensionMakeWithPoints(maxSize.height); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); } - (ASLayoutSize)preferredLayoutSize @@ -268,8 +296,11 @@ do {\ - (void)setPreferredLayoutSize:(ASLayoutSize)preferredLayoutSize { - self.width = preferredLayoutSize.width; - self.height = preferredLayoutSize.height; + ASDN::MutexLocker l(__instanceLock__); + _size.width = preferredLayoutSize.width; + _size.height = preferredLayoutSize.height; + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleHeightProperty); } - (ASLayoutSize)minLayoutSize @@ -280,8 +311,11 @@ do {\ - (void)setMinLayoutSize:(ASLayoutSize)minLayoutSize { - self.minWidth = minLayoutSize.width; - self.minHeight = minLayoutSize.height; + ASDN::MutexLocker l(__instanceLock__); + _size.minWidth = minLayoutSize.width; + _size.minHeight = minLayoutSize.height; + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMinHeightProperty); } - (ASLayoutSize)maxLayoutSize @@ -292,8 +326,11 @@ do {\ - (void)setMaxLayoutSize:(ASLayoutSize)maxLayoutSize { - self.maxWidth = maxLayoutSize.width; - self.maxHeight = maxLayoutSize.height; + ASDN::MutexLocker l(__instanceLock__); + _size.maxWidth = maxLayoutSize.width; + _size.maxHeight = maxLayoutSize.height; + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxWidthProperty); + ASLayoutElementStyleCallDelegate(ASLayoutElementStyleMaxHeightProperty); } @@ -301,122 +338,122 @@ do {\ - (void)setSpacingBefore:(CGFloat)spacingBefore { - ASDN::MutexLocker l(__instanceLock__); - _spacingBefore = spacingBefore; + _spacingBefore.store(spacingBefore); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingBeforeProperty); } - (CGFloat)spacingBefore { - ASDN::MutexLocker l(__instanceLock__); - return _spacingBefore; + return _spacingBefore.load(); } - (void)setSpacingAfter:(CGFloat)spacingAfter { - ASDN::MutexLocker l(__instanceLock__); - _spacingAfter = spacingAfter; + _spacingAfter.store(spacingAfter); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleSpacingAfterProperty); } - (CGFloat)spacingAfter { - ASDN::MutexLocker l(__instanceLock__); - return _spacingAfter; + return _spacingAfter.load(); } - (void)setFlexGrow:(CGFloat)flexGrow { - ASDN::MutexLocker l(__instanceLock__); - _flexGrow = flexGrow; + _flexGrow.store(flexGrow); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexGrowProperty); } - (CGFloat)flexGrow { - ASDN::MutexLocker l(__instanceLock__); - return _flexGrow; + return _flexGrow.load(); } - (void)setFlexShrink:(CGFloat)flexShrink { - ASDN::MutexLocker l(__instanceLock__); - _flexShrink = flexShrink; + _flexShrink.store(flexShrink); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexShrinkProperty); } - (CGFloat)flexShrink { - ASDN::MutexLocker l(__instanceLock__); - return _flexShrink; + return _flexShrink.load(); } - (void)setFlexBasis:(ASDimension)flexBasis { - ASDN::MutexLocker l(__instanceLock__); - _flexBasis = flexBasis; + _flexBasis.store(flexBasis); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleFlexBasisProperty); } - (ASDimension)flexBasis { - ASDN::MutexLocker l(__instanceLock__); - return _flexBasis; + return _flexBasis.load(); } - (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf { - ASDN::MutexLocker l(__instanceLock__); - _alignSelf = alignSelf; + _alignSelf.store(alignSelf); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAlignSelfProperty); } - (ASStackLayoutAlignSelf)alignSelf { - ASDN::MutexLocker l(__instanceLock__); - return _alignSelf; + return _alignSelf.load(); } - (void)setAscender:(CGFloat)ascender { - ASDN::MutexLocker l(__instanceLock__); - _ascender = ascender; + _ascender.store(ascender); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleAscenderProperty); } - (CGFloat)ascender { - ASDN::MutexLocker l(__instanceLock__); - return _ascender; + return _ascender.load(); } - (void)setDescender:(CGFloat)descender { - ASDN::MutexLocker l(__instanceLock__); - _descender = descender; + _descender.store(descender); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleDescenderProperty); } - (CGFloat)descender { - ASDN::MutexLocker l(__instanceLock__); - return _descender; + return _descender.load(); } #pragma mark - ASAbsoluteLayoutElement - (void)setLayoutPosition:(CGPoint)layoutPosition { - ASDN::MutexLocker l(__instanceLock__); - _layoutPosition = layoutPosition; + _layoutPosition.store(layoutPosition); ASLayoutElementStyleCallDelegate(ASLayoutElementStyleLayoutPositionProperty); } - (CGPoint)layoutPosition { - ASDN::MutexLocker l(__instanceLock__); - return _layoutPosition; + return _layoutPosition.load(); } +#pragma mark Deprecated + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +- (ASRelativeSizeRange)sizeRange +{ + return ASRelativeSizeRangeMake(self.minLayoutSize, self.maxLayoutSize); +} + +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange +{ + self.minLayoutSize = sizeRange.min; + self.maxLayoutSize = sizeRange.max; +} + +#pragma clang diagnostic pop + @end diff --git a/AsyncDisplayKit/Layout/ASLayoutElementPrivate.h b/AsyncDisplayKit/Layout/ASLayoutElementPrivate.h index 8dded8c36f..70f3aa917f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutElementPrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutElementPrivate.h @@ -93,3 +93,122 @@ extern void ASLayoutElementClearCurrentContext(); {\ return _ASEnvironmentLayoutOptionsExtensionGetEdgeInsetsAtIndex(self, idx);\ }\ + + + +#pragma mark - ASLayoutElementStyleForwarding (Deprecated) + +// For the time beeing we are forwading all style related properties on ASDisplayNode and ASLayoutSpec. This define +// help us to not have duplicate code while moving from 1.x to 2.0s +#define ASLayoutElementStyleForwarding \ +\ +@dynamic spacingBefore, spacingAfter, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender, sizeRange, layoutPosition;\ +\ +_Pragma("mark - ASStackLayoutElement")\ +\ +- (void)setSpacingBefore:(CGFloat)spacingBefore\ +{\ + self.style.spacingBefore = spacingBefore;\ +}\ +\ +- (CGFloat)spacingBefore\ +{\ + return self.style.spacingBefore;\ +}\ +\ +- (void)setSpacingAfter:(CGFloat)spacingAfter\ +{\ + self.style.spacingAfter = spacingAfter;\ +}\ +\ +- (CGFloat)spacingAfter\ +{\ + return self.style.spacingAfter;\ +}\ +\ +- (void)setFlexGrow:(CGFloat)flexGrow\ +{\ + self.style.flexGrow = flexGrow;\ +}\ +\ +- (CGFloat)flexGrow\ +{\ + return self.style.flexGrow;\ +}\ +\ +- (void)setFlexShrink:(CGFloat)flexShrink\ +{\ + self.style.flexShrink = flexShrink;\ +}\ +\ +- (CGFloat)flexShrink\ +{\ + return self.style.flexShrink;\ +}\ +\ +- (void)setFlexBasis:(ASDimension)flexBasis\ +{\ + self.style.flexBasis = flexBasis;\ +}\ +\ +- (ASDimension)flexBasis\ +{\ + return self.style.flexBasis;\ +}\ +\ +- (void)setAlignSelf:(ASStackLayoutAlignSelf)alignSelf\ +{\ + self.style.alignSelf = alignSelf;\ +}\ +\ +- (ASStackLayoutAlignSelf)alignSelf\ +{\ + return self.style.alignSelf;\ +}\ +\ +- (void)setAscender:(CGFloat)ascender\ +{\ + self.style.ascender = ascender;\ +}\ +\ +- (CGFloat)ascender\ +{\ + return self.style.ascender;\ +}\ +\ +- (void)setDescender:(CGFloat)descender\ +{\ + self.style.descender = descender;\ +}\ +\ +- (CGFloat)descender\ +{\ + return self.style.descender;\ +}\ +\ +_Pragma("mark - ASAbsoluteLayoutElement")\ +\ +- (void)setLayoutPosition:(CGPoint)layoutPosition\ +{\ + self.style.layoutPosition = layoutPosition;\ +}\ +\ +- (CGPoint)layoutPosition\ +{\ + return self.style.layoutPosition;\ +}\ +\ +_Pragma("clang diagnostic push")\ +_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\ +\ +- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\ +{\ + self.style.sizeRange = sizeRange;\ +}\ +\ +- (ASRelativeSizeRange)sizeRange\ +{\ + return self.style.sizeRange;\ +}\ +\ +_Pragma("clang diagnostic pop")\ diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h index 60c66a9106..84d5a9db35 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h @@ -61,4 +61,15 @@ NS_ASSUME_NONNULL_BEGIN @end +@interface ASLayout () + +/** + * Position in parent. Default to CGPointNull. + * + * @discussion When being used as a sublayout, this property must not equal CGPointNull. + */ +@property (nonatomic, assign, readwrite) CGPoint position; + +@end + NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm index 517f25aac9..ed186d4a0c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm @@ -88,10 +88,6 @@ // Replace object at the given index with the layoutElement _childrenArray[index] = layoutElement; - - // TODO: Should we propagate up the layoutElement at it could happen that multiple children will propagated up their - // layout options and one child will overwrite values from another child - // [self propagateUpLayoutElement:finalLayoutElement]; } - (id)childAtIndex:(NSUInteger)index diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 261066b017..70914c4a41 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -16,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN /** * 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 @@ -63,19 +63,31 @@ NS_ASSUME_NONNULL_BEGIN @end /** - * An ASLayoutSpec subclass that can wrap a ASLayoutElement and calculates the layout of the child. + * An ASLayoutSpec subclass that can wrap one or more ASLayoutElement and calculates the layout based on the + * sizes of the children. If multiple children are provided the size of the biggest child will be used to for + * size of this layout spec. */ @interface ASWrapperLayoutSpec : ASLayoutSpec /* - * Returns an ASWrapperLayoutSpec object with the given layoutElement as child + * Returns an ASWrapperLayoutSpec object with the given layoutElement as child. */ + (instancetype)wrapperWithLayoutElement:(id)layoutElement AS_WARN_UNUSED_RESULT; /* - * Returns an ASWrapperLayoutSpec object initialized with the given layoutElement as child + * Returns an ASWrapperLayoutSpec object with the given layoutElements as children. */ -- (instancetype)initWithLayoutElement:(id)layoutElement NS_DESIGNATED_INITIALIZER;; ++ (instancetype)wrapperWithLayoutElements:(NSArray> *)layoutElements AS_WARN_UNUSED_RESULT; + +/* + * Returns an ASWrapperLayoutSpec object initialized with the given layoutElement as child. + */ +- (instancetype)initWithLayoutElement:(id)layoutElement AS_WARN_UNUSED_RESULT; + +/* + * Returns an ASWrapperLayoutSpec object initialized with the given layoutElements as children. + */ +- (instancetype)initWithLayoutElements:(NSArray> *)layoutElements AS_WARN_UNUSED_RESULT; /* * Init not available for ASWrapperLayoutSpec diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index bf8b583b44..ef9643e44e 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -11,13 +11,13 @@ #import "ASLayoutSpec.h" #import "ASLayoutSpecPrivate.h" #import "ASLayoutSpec+Subclasses.h" - #import "ASLayoutElementStylePrivate.h" @implementation ASLayoutSpec // Dynamic properties for ASLayoutElements @dynamic layoutElementType; +@synthesize debugName = _debugName; @synthesize isFinalLayoutElement = _isFinalLayoutElement; #pragma mark - Class @@ -82,12 +82,6 @@ #pragma mark - Layout -// Deprecated -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize -{ - return [self layoutThatFits:constrainedSize]; -} - - (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize { return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max]; @@ -111,31 +105,17 @@ return [ASLayout layoutWithLayoutElement:self size:constrainedSize.min]; } - -#pragma mark - Parent - -- (void)setParent:(id)parent -{ - // FIXME: Locking should be evaluated here. _parent is not widely used yet, though. - _parent = parent; - - if ([parent supportsUpwardPropagation]) { - ASEnvironmentStatePropagateUp(parent, self.environmentState.layoutOptionsState); - } -} - #pragma mark - Child - (void)setChild:(id)child { - ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");; + ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API"); if (child) { id finalLayoutElement = [self layoutElementToAddFromLayoutElement:child]; if (finalLayoutElement) { _childrenArray[0] = finalLayoutElement; - [self propagateUpLayoutElement:finalLayoutElement]; } } else { if (_childrenArray.count) { @@ -148,11 +128,7 @@ { ASDisplayNodeAssert(_childrenArray.count < 2, @"This layout spec does not support more than one child. Use the setChildren: or the setChild:AtIndex: API"); - if (_childrenArray.count) { - return _childrenArray[0]; - } - - return nil; + return _childrenArray.firstObject; } #pragma mark - Children @@ -195,28 +171,11 @@ _environmentState = environmentState; } -// Subclasses can override this method to return NO, because upward propagation is not enabled if a layout -// specification has more than one child. Currently ASStackLayoutSpec and ASAbsoluteLayoutSpec are currently -// the specifications that are known to have more than one. -- (BOOL)supportsUpwardPropagation -{ - return ASEnvironmentStatePropagationEnabled(); -} - - (BOOL)supportsTraitsCollectionPropagation { return ASEnvironmentStateTraitCollectionPropagationEnabled(); } -- (void)propagateUpLayoutElement:(id)layoutElement -{ - if ([layoutElement isKindOfClass:[ASLayoutSpec class]]) { - [(ASLayoutSpec *)layoutElement setParent:self]; // This will trigger upward propogation if needed. - } else if ([self supportsUpwardPropagation]) { - ASEnvironmentStatePropagateUp(self, layoutElement.environmentState.layoutOptionsState); // Probably an ASDisplayNode - } -} - - (ASEnvironmentTraitCollection)environmentTraitCollection { return _environmentState.environmentTraitCollection; @@ -235,6 +194,76 @@ ASEnvironmentLayoutExtensibilityForwarding +#pragma mark - Framework Private + +- (nullable NSSet> *)findDuplicatedElementsInSubtree +{ + NSMutableSet *result = nil; + NSUInteger count = 0; + [self _findDuplicatedElementsInSubtreeWithWorkingSet:[[NSMutableSet alloc] init] workingCount:&count result:&result]; + return result; +} + +/** + * This method is extremely performance-sensitive, so we do some strange things. + * + * @param workingSet A working set of elements for use in the recursion. + * @param workingCount The current count of the set for use in the recursion. + * @param result The set into which to put the result. This initially points to @c nil to save time if no duplicates exist. + */ +- (void)_findDuplicatedElementsInSubtreeWithWorkingSet:(NSMutableSet> *)workingSet workingCount:(NSUInteger *)workingCount result:(NSMutableSet> * _Nullable *)result +{ + Class layoutSpecClass = [ASLayoutSpec class]; + + for (id child in self) { + // Add the object into the set. + [workingSet addObject:child]; + + // Check that addObject: caused the count to increase. + // This is faster than using containsObject. + NSUInteger oldCount = *workingCount; + NSUInteger newCount = workingSet.count; + BOOL objectAlreadyExisted = (newCount != oldCount + 1); + if (objectAlreadyExisted) { + if (*result == nil) { + *result = [[NSMutableSet alloc] init]; + } + [*result addObject:child]; + } else { + *workingCount = newCount; + // If child is a layout spec we haven't visited, recurse its children. + if ([child isKindOfClass:layoutSpecClass]) { + [(ASLayoutSpec *)child _findDuplicatedElementsInSubtreeWithWorkingSet:workingSet workingCount:workingCount result:result]; + } + } + } +} + +#pragma mark - Debugging + +- (NSString *)debugName +{ + ASDN::MutexLocker l(__instanceLock__); + return _debugName; +} + +- (void)setDebugName:(NSString *)debugName +{ + ASDN::MutexLocker l(__instanceLock__); + if (!ASObjectIsEqual(_debugName, debugName)) { + _debugName = [debugName copy]; + } +} + +#pragma mark - Deprecated + +- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +{ + return [self layoutThatFits:constrainedSize]; +} + +ASLayoutElementStyleForwarding + @end #pragma mark - ASWrapperLayoutSpec @@ -255,16 +284,41 @@ ASEnvironmentLayoutExtensibilityForwarding return self; } ++ (instancetype)wrapperWithLayoutElements:(NSArray> *)layoutElements +{ + return [[self alloc] initWithLayoutElements:layoutElements]; +} + +- (instancetype)initWithLayoutElements:(NSArray> *)layoutElements +{ + self = [super init]; + if (self) { + self.children = layoutElements; + } + return self; +} + - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { - ASLayout *sublayout = [self.child layoutThatFits:constrainedSize parentSize:constrainedSize.max]; - sublayout.position = CGPointZero; - return [ASLayout layoutWithLayoutElement:self size:sublayout.size sublayouts:@[sublayout]]; + NSArray *children = self.children; + NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count]; + + CGSize size = constrainedSize.min; + for (id child in children) { + ASLayout *sublayout = [child layoutThatFits:constrainedSize parentSize:constrainedSize.max]; + sublayout.position = CGPointZero; + + size.width = MAX(size.width, sublayout.size.width); + size.height = MAX(size.height, sublayout.size.height); + + [sublayouts addObject:sublayout]; + } + + return [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts]; } @end - #pragma mark - ASLayoutSpec (Debugging) @implementation ASLayoutSpec (Debugging) @@ -299,7 +353,11 @@ ASEnvironmentLayoutExtensibilityForwarding - (NSString *)asciiArtName { - return NSStringFromClass([self class]); + NSString *string = NSStringFromClass([self class]); + if (_debugName) { + string = [string stringByAppendingString:[NSString stringWithFormat:@" (debugName = %@)",_debugName]]; + } + return string; } @end diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index dbd329cceb..b38fe83a86 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -12,7 +12,7 @@ #import "ASLayoutSpec+Subclasses.h" #import "ASAssert.h" -#import "ASLayout.h" +#import "ASLayoutSpec+Subclasses.h" static NSUInteger const kUnderlayChildIndex = 0; static NSUInteger const kOverlayChildIndex = 1; diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index c5cd3372dd..1155af720e 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -17,13 +17,17 @@ #import "ASAssert.h" #import "ASInternalHelpers.h" -#import "ASLayout.h" +#import "ASLayoutSpec+Subclasses.h" + +#pragma mark - ASRatioLayoutSpec @implementation ASRatioLayoutSpec { CGFloat _ratio; } +#pragma mark - Lifecycle + + (instancetype)ratioLayoutSpecWithRatio:(CGFloat)ratio child:(id)child { return [[self alloc] initWithRatio:ratio child:child]; @@ -34,31 +38,37 @@ 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; - [self setChild:child]; + self.child = child; + return self; } +#pragma mark - Setter / Getter + - (void)setRatio:(CGFloat)ratio { ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable"); _ratio = ratio; } +#pragma mark - ASLayoutElement + - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { std::vector sizeOptions; - // TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used - if (!isinf(constrainedSize.max.width) && ASPointsValidForLayout(constrainedSize.max.width)) { + + if (ASPointsValidForSize(constrainedSize.max.width)) { sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, { constrainedSize.max.width, ASFloorPixelValue(_ratio * constrainedSize.max.width) })); } - // TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used - if (!isinf(constrainedSize.max.height) && ASPointsValidForLayout(constrainedSize.max.width)) { + + if (ASPointsValidForSize(constrainedSize.max.width)) { sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, { ASFloorPixelValue(constrainedSize.max.height / _ratio), constrainedSize.max.height @@ -71,7 +81,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); + const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeIntersect(constrainedSize, ASSizeRangeMake(*bestSize, *bestSize)); const CGSize parentSize = (bestSize == sizeOptions.end()) ? ASLayoutElementParentSizeUndefined : *bestSize; ASLayout *sublayout = [self.child layoutThatFits:childRange parentSize:parentSize]; sublayout.position = CGPointZero; @@ -80,6 +90,8 @@ @end +#pragma mark - ASRatioLayoutSpec (Debugging) + @implementation ASRatioLayoutSpec (Debugging) #pragma mark - ASLayoutElementAsciiArtProtocol diff --git a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h index 17f3ccfffd..31e6350849 100644 --- a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h @@ -13,25 +13,27 @@ #import /** How the child is positioned within the spec. */ -typedef NS_OPTIONS(NSUInteger, ASRelativeLayoutSpecPosition) { - /** The child is positioned at point 0 relatively to the layout axis (ie left / top most) */ - ASRelativeLayoutSpecPositionStart = 0, - /** The child is centered along the specified axis */ - ASRelativeLayoutSpecPositionCenter = 1 << 0, - /** The child is positioned at the maximum point of the layout axis (ie right / bottom most) */ - ASRelativeLayoutSpecPositionEnd = 1 << 1, +typedef NS_ENUM(NSUInteger, ASRelativeLayoutSpecPosition) { + /** The child is positioned at point 0 */ + ASRelativeLayoutSpecPositionNone = 0, + /** The child is positioned at point 0 relatively to the layout axis (ie left / top most) */ + ASRelativeLayoutSpecPositionStart = 1, + /** The child is centered along the specified axis */ + ASRelativeLayoutSpecPositionCenter = 2, + /** The child is positioned at the maximum point of the layout axis (ie right / bottom most) */ + ASRelativeLayoutSpecPositionEnd = 3, }; /** How much space the spec will take up. */ typedef NS_OPTIONS(NSUInteger, ASRelativeLayoutSpecSizingOption) { - /** The spec will take up the maximum size possible */ - ASRelativeLayoutSpecSizingOptionDefault, - /** The spec will take up the minimum size possible along the X axis */ - ASRelativeLayoutSpecSizingOptionMinimumWidth = 1 << 0, - /** The spec will take up the minimum size possible along the Y axis */ - ASRelativeLayoutSpecSizingOptionMinimumHeight = 1 << 1, - /** Convenience option to take up the minimum size along both the X and Y axis */ - ASRelativeLayoutSpecSizingOptionMinimumSize = ASRelativeLayoutSpecSizingOptionMinimumWidth | ASRelativeLayoutSpecSizingOptionMinimumHeight, + /** The spec will take up the maximum size possible */ + ASRelativeLayoutSpecSizingOptionDefault, + /** The spec will take up the minimum size possible along the X axis */ + ASRelativeLayoutSpecSizingOptionMinimumWidth = 1 << 0, + /** The spec will take up the minimum size possible along the Y axis */ + ASRelativeLayoutSpecSizingOptionMinimumHeight = 1 << 1, + /** Convenience option to take up the minimum size along both the X and Y axis */ + ASRelativeLayoutSpecSizingOptionMinimumSize = ASRelativeLayoutSpecSizingOptionMinimumWidth | ASRelativeLayoutSpecSizingOptionMinimumHeight, }; NS_ASSUME_NONNULL_BEGIN diff --git a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm index 07e0324be0..1072fb443d 100644 --- a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm @@ -13,7 +13,7 @@ #import "ASRelativeLayoutSpec.h" #import "ASInternalHelpers.h" -#import "ASLayout.h" +#import "ASLayoutSpec+Subclasses.h" @implementation ASRelativeLayoutSpec @@ -54,31 +54,21 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { - // If we have a finite size in any direction, pass this so that the child can - // resolve percentages against it. Otherwise pass ASLayoutElementParentDimensionUndefined - // as the size will depend on the content - // TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used + // If we have a finite size in any direction, pass this so that the child can resolve percentages against it. + // Otherwise pass ASLayoutElementParentDimensionUndefined as the size will depend on the content CGSize size = { - isinf(constrainedSize.max.width) || !ASPointsValidForLayout(constrainedSize.max.width) ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, - isinf(constrainedSize.max.height) || !ASPointsValidForLayout(constrainedSize.max.height) ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height + ASPointsValidForSize(constrainedSize.max.width) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.width, + ASPointsValidForSize(constrainedSize.max.height) == NO ? ASLayoutElementParentDimensionUndefined : constrainedSize.max.height }; - BOOL reduceWidth = (_horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0 || - (_horizontalPosition & ASRelativeLayoutSpecPositionEnd) != 0; - - BOOL reduceHeight = (_verticalPosition & ASRelativeLayoutSpecPositionCenter) != 0 || - (_verticalPosition & ASRelativeLayoutSpecPositionEnd) != 0; - // Layout the child const CGSize minChildSize = { - reduceWidth ? 0 : constrainedSize.min.width, - reduceHeight ? 0 : constrainedSize.min.height, + (_horizontalPosition != ASRelativeLayoutSpecPositionNone) ? 0 : constrainedSize.min.width, + (_verticalPosition != ASRelativeLayoutSpecPositionNone) ? 0 : constrainedSize.min.height, }; - ASLayout *sublayout = [self.child layoutThatFits:ASSizeRangeMake(minChildSize, constrainedSize.max) parentSize:size]; - // If we have an undetermined height or width, use the child size to define the layout - // size + // If we have an undetermined height or width, use the child size to define the layout size size = ASSizeRangeClamp(constrainedSize, { isfinite(size.width) == NO ? sublayout.size.width : size.width, isfinite(size.height) == NO ? sublayout.size.height : size.height @@ -104,9 +94,9 @@ - (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position { - if ((position & ASRelativeLayoutSpecPositionCenter) != 0) { + if (position == ASRelativeLayoutSpecPositionCenter) { return 0.5f; - } else if ((position & ASRelativeLayoutSpecPositionEnd) != 0) { + } else if (position == ASRelativeLayoutSpecPositionEnd) { return 1.0f; } else { return 0.0f; diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 463327dcf6..2f261b681f 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -182,15 +182,6 @@ @end -@implementation ASStackLayoutSpec (ASEnvironment) - -- (BOOL)supportsUpwardPropagation -{ - return NO; -} - -@end - @implementation ASStackLayoutSpec (Debugging) #pragma mark - ASLayoutElementAsciiArtProtocol diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index 9fedcb1b4d..484b6b76a9 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -120,7 +120,7 @@ } } }; - [displayBlocks addObject:[pushAndDisplayBlock copy]]; + [displayBlocks addObject:pushAndDisplayBlock]; } // Recursively capture displayBlocks for all descendants. @@ -130,11 +130,16 @@ // If we pushed a transform, pop it by adding a display block that does nothing other than that. if (shouldDisplay) { - dispatch_block_t popBlock = ^{ - CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextRestoreGState(context); - }; - [displayBlocks addObject:[popBlock copy]]; + // Since this block is pure, we can store it statically. + static dispatch_block_t popBlock; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + popBlock = ^{ + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextRestoreGState(context); + }; + }); + [displayBlocks addObject:popBlock]; } } @@ -252,7 +257,7 @@ }; } - return [displayBlock copy]; + return displayBlock; } - (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm index 34b1aaaef0..6fbc9396e9 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm @@ -153,6 +153,9 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme if (parentLayoutOptionsState.ascender == defaultState.ascender) { parentLayoutOptionsState.ascender = layoutOptionsState.ascender; } + if (parentLayoutOptionsState.descender == defaultState.descender) { + parentLayoutOptionsState.descender = layoutOptionsState.descender; + } if (CGPointEqualToPoint(parentLayoutOptionsState.layoutPosition, defaultState.layoutPosition)) { // For now it is unclear if we should be up-propagating sizeRange or layoutPosition. diff --git a/AsyncDisplayKit/Private/ASLayoutSpecPrivate.h b/AsyncDisplayKit/Private/ASLayoutSpecPrivate.h index 7cea216551..8a606483f1 100644 --- a/AsyncDisplayKit/Private/ASLayoutSpecPrivate.h +++ b/AsyncDisplayKit/Private/ASLayoutSpecPrivate.h @@ -15,11 +15,20 @@ #import "ASEnvironmentInternal.h" #import "ASThread.h" +NS_ASSUME_NONNULL_BEGIN + @interface ASLayoutSpec() { ASDN::RecursiveMutex __instanceLock__; ASEnvironmentState _environmentState; ASLayoutElementStyle *_style; NSMutableArray *_childrenArray; } + +/** + * Recursively search the subtree for elements that occur more than once. + */ +- (nullable NSSet> *)findDuplicatedElementsInSubtree; + @end +NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm index 5956568105..081d7cf42c 100644 --- a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm @@ -11,6 +11,7 @@ #import "ASStackBaselinePositionedLayout.h" #import "ASLayoutSpecUtilities.h" +#import "ASLayoutSpec+Subclasses.h" static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASLayout *layout) { diff --git a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm index 4cb5ca52ee..f00478b67a 100644 --- a/AsyncDisplayKit/Private/ASStackPositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackPositionedLayout.mm @@ -14,6 +14,7 @@ #import "ASInternalHelpers.h" #import "ASLayoutSpecUtilities.h" +#import "ASLayoutSpec+Subclasses.h" static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, const ASStackUnpositionedItem &l, diff --git a/AsyncDisplayKit/Private/_AS-objc-internal.h b/AsyncDisplayKit/Private/_AS-objc-internal.h index f050602a19..535c212e5e 100644 --- a/AsyncDisplayKit/Private/_AS-objc-internal.h +++ b/AsyncDisplayKit/Private/_AS-objc-internal.h @@ -441,7 +441,7 @@ typedef enum { -(BOOL)_tryRetain { \ __typeof__(_rc_ivar) _prev; \ do { \ - _prev = __atomic_load_n(&_rc_ivar, __ATOMIC_SEQ_CST);; \ + _prev = __atomic_load_n(&_rc_ivar, __ATOMIC_SEQ_CST); \ if (_prev & 1) { \ return 0; \ } else if (_prev == -2) { \ diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm index 0beb67902c..d8ce5f84b5 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm +++ b/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm @@ -35,11 +35,20 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() return truncationCharacterSet; } +@interface ASTextKitRenderer() +/** + * This object is lazily created. It is provided to the NSAttributedString + * drawing methods used by the fast-paths in the size calculation and drawing + * instance methods. + */ +@property (nonatomic, strong, readonly) NSStringDrawingContext *stringDrawingContext; +@end + @implementation ASTextKitRenderer { CGSize _calculatedSize; BOOL _sizeIsCalculated; } -@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater, fontSizeAdjuster = _fontSizeAdjuster; +@synthesize attributes = _attributes, context = _context, shadower = _shadower, truncater = _truncater, fontSizeAdjuster = _fontSizeAdjuster, stringDrawingContext = _stringDrawingContext; #pragma mark - Initialization @@ -50,6 +59,7 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() _constrainedSize = constrainedSize; _attributes = attributes; _sizeIsCalculated = NO; + _currentScaleFactor = 1; } return self; } @@ -106,6 +116,19 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() return _context; } +- (NSStringDrawingContext *)stringDrawingContext +{ + if (_stringDrawingContext == nil) { + _stringDrawingContext = [[NSStringDrawingContext alloc] init]; + + if (isinf(_constrainedSize.width) == NO && _attributes.maximumNumberOfLines > 0) { + ASDisplayNodeAssert(_attributes.maximumNumberOfLines != 1, @"Max line count 1 is not supported in fast-path."); + [_stringDrawingContext setValue:@(_attributes.maximumNumberOfLines) forKey:@"maximumNumberOfLines"]; + } + } + return _stringDrawingContext; +} + #pragma mark - Sizing - (CGSize)size @@ -144,9 +167,18 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() if (isinf(_constrainedSize.width) == NO && [_attributes.pointSizeScaleFactors count] > 0) { _currentScaleFactor = [[self fontSizeAdjuster] scaleFactor]; } - - __block NSTextStorage *scaledTextStorage = nil; + + // If we do not scale, do exclusion, or do custom truncation, we should just use NSAttributedString drawing for a fast-path. + if (self.canUseFastPath) { + CGRect rect = [_attributes.attributedString boundingRectWithSize:_constrainedSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:self.stringDrawingContext]; + // Intersect with constrained rect, in case text kit goes out-of-bounds. + rect = CGRectIntersection(rect, {CGPointZero, _constrainedSize}); + _calculatedSize = [self.shadower outsetSizeWithInsetSize:rect.size]; + return; + } + BOOL isScaled = [self isScaled]; + __block NSTextStorage *scaledTextStorage = nil; if (isScaled) { // apply the string scale before truncating or else we may truncate the string after we've done the work to shrink it. [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { @@ -161,15 +193,13 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() [[self truncater] truncate]; + CGRect constrainedRect = {CGPointZero, _constrainedSize}; + __block CGRect boundingRect; + // Force glyph generation and layout, which may not have happened yet (and isn't triggered by // -usedRectForTextContainer:). [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { [layoutManager ensureLayoutForTextContainer:textContainer]; - }]; - - CGRect constrainedRect = {CGPointZero, _constrainedSize}; - __block CGRect boundingRect; - [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { boundingRect = [layoutManager usedRectForTextContainer:textContainer]; if (isScaled) { // put the non-scaled version back @@ -181,13 +211,33 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() // TextKit often returns incorrect glyph bounding rects in the horizontal direction, so we clip to our bounding rect // to make sure our width calculations aren't being offset by glyphs going beyond the constrained rect. boundingRect = CGRectIntersection(boundingRect, {.size = constrainedRect.size}); - CGSize boundingSize = [_shadower outsetSizeWithInsetSize:boundingRect.size]; - _calculatedSize = CGSizeMake(boundingSize.width, boundingSize.height); + _calculatedSize = [_shadower outsetSizeWithInsetSize:boundingRect.size]; } - (BOOL)isScaled { - return (self.currentScaleFactor > 0 && self.currentScaleFactor < 1.0); + return (_currentScaleFactor > 0 && _currentScaleFactor < 1.0); +} + +- (BOOL)usesCustomTruncation +{ + // NOTE: This code does not correctly handle if they set `…` with different attributes. + return _attributes.avoidTailTruncationSet != nil || [_attributes.truncationAttributedString.string isEqualToString:@"\u2026"] == NO; +} + +- (BOOL)usesExclusionPaths +{ + return _attributes.exclusionPaths.count > 0; +} + +- (BOOL)canUseFastPath +{ + return self.isScaled == NO + && self.usesCustomTruncation == NO + && self.usesExclusionPaths == NO + // NSAttributedString drawing methods ignore usesLineFragmentOrigin if max line count 1, + // rendering them useless: + && (_attributes.maximumNumberOfLines != 1 || isinf(_constrainedSize.width)); } #pragma mark - Drawing @@ -198,11 +248,12 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() ASDisplayNodeAssertNotNil(context, @"This is no good without a context."); // This renderer may not be the one that did the sizing. If that is the case its truncation and currentScaleFactor may not have been evaluated. - // If there's any possibility we need to truncate or scale (e.g. width is not infinite, perform the size calculation. + // If there's any possibility we need to truncate or scale (i.e. width is not infinite), perform the size calculation. if (_sizeIsCalculated == NO && isinf(_constrainedSize.width) == NO) { [self _calculateSize]; } + bounds = CGRectIntersection(bounds, { .size = _constrainedSize }); CGRect shadowInsetBounds = [[self shadower] insetRectWithConstrainedRect:bounds]; CGContextSaveGState(context); @@ -210,35 +261,47 @@ static NSCharacterSet *_defaultAvoidTruncationCharacterSet() UIGraphicsPushContext(context); LOG(@"%@, shadowInsetBounds = %@",self, NSStringFromCGRect(shadowInsetBounds)); - - [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { - - NSTextStorage *scaledTextStorage = nil; - BOOL isScaled = [self isScaled]; - if (isScaled) { - // if we are going to scale the text, swap out the non-scaled text for the scaled version. - NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage]; - [ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:_currentScaleFactor]; - scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString]; + // If we use default options, we can use NSAttributedString for a + // fast path. + if (self.canUseFastPath) { + CGRect drawingBounds = shadowInsetBounds; + // Add a fudge-factor to the height, to workaround a bug in iOS 7 + if (AS_AT_LEAST_IOS8 == NO) { + drawingBounds.size.height += 3; + } + [_attributes.attributedString drawWithRect:drawingBounds options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingTruncatesLastVisibleLine context:self.stringDrawingContext]; + } else { + BOOL isScaled = [self isScaled]; + [[self context] performBlockWithLockedTextKitComponents:^(NSLayoutManager *layoutManager, NSTextStorage *textStorage, NSTextContainer *textContainer) { - [textStorage removeLayoutManager:layoutManager]; - [scaledTextStorage addLayoutManager:layoutManager]; - } - - LOG(@"usedRect: %@", NSStringFromCGRect([layoutManager usedRectForTextContainer:textContainer])); - NSRange glyphRange = [layoutManager glyphRangeForBoundingRect:CGRectMake(0,0,textContainer.size.width, textContainer.size.height) inTextContainer:textContainer]; - LOG(@"boundingRect: %@", NSStringFromCGRect([layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer])); - - [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; - [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; - - if (isScaled) { - // put the non-scaled version back - [scaledTextStorage removeLayoutManager:layoutManager]; - [textStorage addLayoutManager:layoutManager]; - } - }]; + NSTextStorage *scaledTextStorage = nil; + + if (isScaled) { + // if we are going to scale the text, swap out the non-scaled text for the scaled version. + NSMutableAttributedString *scaledString = [[NSMutableAttributedString alloc] initWithAttributedString:textStorage]; + [ASTextKitFontSizeAdjuster adjustFontSizeForAttributeString:scaledString withScaleFactor:_currentScaleFactor]; + scaledTextStorage = [[NSTextStorage alloc] initWithAttributedString:scaledString]; + + [textStorage removeLayoutManager:layoutManager]; + [scaledTextStorage addLayoutManager:layoutManager]; + } + + LOG(@"usedRect: %@", NSStringFromCGRect([layoutManager usedRectForTextContainer:textContainer])); + + NSRange glyphRange = [layoutManager glyphRangeForBoundingRect:CGRectMake(0,0,textContainer.size.width, textContainer.size.height) inTextContainer:textContainer]; + LOG(@"boundingRect: %@", NSStringFromCGRect([layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:textContainer])); + + [layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; + [layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:shadowInsetBounds.origin]; + + if (isScaled) { + // put the non-scaled version back + [scaledTextStorage removeLayoutManager:layoutManager]; + [textStorage addLayoutManager:layoutManager]; + } + }]; + } UIGraphicsPopContext(); CGContextRestoreGState(context); diff --git a/AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m b/AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m index 24231dde26..93c5c72e44 100644 --- a/AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m +++ b/AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m @@ -63,7 +63,8 @@ ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild: - [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:children] + [ASAbsoluteLayoutSpec + absoluteLayoutSpecWithChildren:children] background:backgroundNode]; [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index f4c7e229a1..443e1d7534 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -377,46 +377,49 @@ - (void)testThatItThrowsIfNodeConstrainedSizeIsImplementedOnDataSourceButNotOnDelegateLayoutInspector { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + ASCollectionNode *node = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + ASCollectionView *collectionView = node.view; id dataSourceAndDelegate = [OCMockObject mockForProtocol:@protocol(InspectorTestDataSourceDelegateProtocol)]; ASSizeRange constrainedSize = ASSizeRangeMake(CGSizeZero, CGSizeZero); NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; NSValue *value = [NSValue value:&constrainedSize withObjCType:@encode(ASSizeRange)]; - [[[dataSourceAndDelegate stub] andReturnValue:value] collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; - collectionView.asyncDataSource = dataSourceAndDelegate; + [[[dataSourceAndDelegate stub] andReturnValue:value] collectionNode:node constrainedSizeForItemAtIndexPath:indexPath]; + node.dataSource = dataSourceAndDelegate; id delegate = [InspectorTestDataSourceDelegateWithoutNodeConstrainedSize new]; - collectionView.asyncDelegate = delegate; + node.delegate = delegate; ASCollectionViewLayoutInspector *inspector = [[ASCollectionViewLayoutInspector alloc] initWithCollectionView:collectionView]; collectionView.layoutInspector = inspector; XCTAssertThrows([inspector collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]); - collectionView.asyncDelegate = dataSourceAndDelegate; + node.delegate = dataSourceAndDelegate; XCTAssertNoThrow([inspector collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]); } - (void)testThatItThrowsIfNodeConstrainedSizeIsImplementedOnDataSourceButNotOnDelegateFlowLayoutInspector { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + ASCollectionNode *node = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + ASCollectionView *collectionView = node.view; id dataSourceAndDelegate = [OCMockObject mockForProtocol:@protocol(InspectorTestDataSourceDelegateProtocol)]; ASSizeRange constrainedSize = ASSizeRangeMake(CGSizeZero, CGSizeZero); NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; NSValue *value = [NSValue value:&constrainedSize withObjCType:@encode(ASSizeRange)]; - [[[dataSourceAndDelegate stub] andReturnValue:value] collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]; - collectionView.asyncDataSource = dataSourceAndDelegate; + [[[dataSourceAndDelegate stub] andReturnValue:value] collectionNode:node constrainedSizeForItemAtIndexPath:indexPath]; + node.dataSource = dataSourceAndDelegate; id delegate = [InspectorTestDataSourceDelegateWithoutNodeConstrainedSize new]; - collectionView.asyncDelegate = delegate; + node.delegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = collectionView.layoutInspector; + XCTAssertThrows([inspector collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]); - collectionView.asyncDelegate = dataSourceAndDelegate; + node.delegate = dataSourceAndDelegate; XCTAssertNoThrow([inspector collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath]); } diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.mm b/AsyncDisplayKitTests/ASCollectionViewTests.mm index 555588a394..f0f509195e 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.mm +++ b/AsyncDisplayKitTests/ASCollectionViewTests.mm @@ -210,6 +210,34 @@ XCTAssertEqualObjects([collectionView supplementaryNodeKindsInDataController:nil], @[UICollectionElementKindSectionHeader]); } +- (void)testReloadIfNeeded +{ + [ASDisplayNode setSuppressesInvalidCollectionUpdateExceptions:NO]; + + __block ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; + __block ASCollectionViewTestDelegate *del = testController.asyncDelegate; + __block ASCollectionNode *cn = testController.collectionNode; + + void (^reset)() = ^void() { + testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; + del = testController.asyncDelegate; + cn = testController.collectionNode; + }; + + // Check if the number of sections matches the data source + XCTAssertEqual(cn.numberOfSections, del->_itemCounts.size(), @"Section count doesn't match the data source"); + + // Reset everything and then check if numberOfItemsInSection matches the data source + reset(); + XCTAssertEqual([cn numberOfItemsInSection:0], del->_itemCounts[0], @"Number of items in Section doesn't match the data source"); + + // Reset and check if we can get the node corresponding to a specific indexPath + reset(); + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; + ASTextCellNodeWithSetSelectedCounter *node = (ASTextCellNodeWithSetSelectedCounter*)[cn nodeForItemAtIndexPath:indexPath]; + XCTAssertTrue([node.text isEqualToString:indexPath.description], @"Node's text should match the initial text it was created with"); +} + - (void)testSelection { ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; @@ -222,27 +250,60 @@ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; ASCellNode *node = [testController.collectionView nodeForItemAtIndexPath:indexPath]; - + + NSInteger setSelectedCount = 0; // selecting node should select cell node.selected = YES; + ++setSelectedCount; XCTAssertTrue([[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath], @"Selecting node should update cell selection."); // deselecting node should deselect cell node.selected = NO; + ++setSelectedCount; XCTAssertTrue([[testController.collectionView indexPathsForSelectedItems] isEqualToArray:@[]], @"Deselecting node should update cell selection."); - // selecting cell via collectionView should select node - [testController.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + // selecting cell via collectionNode should select node + ++setSelectedCount; + [testController.collectionNode selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; XCTAssertTrue(node.isSelected == YES, @"Selecting cell should update node selection."); - // deselecting cell via collectionView should deselect node - [testController.collectionView deselectItemAtIndexPath:indexPath animated:NO]; + // deselecting cell via collectionNode should deselect node + ++setSelectedCount; + [testController.collectionNode deselectItemAtIndexPath:indexPath animated:NO]; XCTAssertTrue(node.isSelected == NO, @"Deselecting cell should update node selection."); // select the cell again, scroll down and back up, and check that the state persisted - [testController.collectionView selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + [testController.collectionNode selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + ++setSelectedCount; XCTAssertTrue(node.isSelected == YES, @"Selecting cell should update node selection."); - + + testController.collectionNode.allowsMultipleSelection = YES; + + NSIndexPath *indexPath2 = [NSIndexPath indexPathForItem:1 inSection:0]; + ASCellNode *node2 = [testController.collectionView nodeForItemAtIndexPath:indexPath2]; + + // selecting cell via collectionNode should select node + [testController.collectionNode selectItemAtIndexPath:indexPath2 animated:NO scrollPosition:UICollectionViewScrollPositionNone]; + XCTAssertTrue(node2.isSelected == YES, @"Selecting cell should update node selection."); + + XCTAssertTrue([[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath] && + [[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath2], + @"Selecting multiple cells should result in those cells being in the array of selectedItems."); + + // deselecting node should deselect cell + node.selected = NO; + ++setSelectedCount; + XCTAssertTrue(![[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath] && + [[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath2], @"Deselecting node should update array of selectedItems."); + + node.selected = YES; + ++setSelectedCount; + XCTAssertTrue([[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath], @"Selecting node should update cell selection."); + + node2.selected = NO; + XCTAssertTrue([[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath] && + ![[testController.collectionView indexPathsForSelectedItems] containsObject:indexPath2], @"Deselecting node should update array of selectedItems."); + // reload cell (-prepareForReuse is called) & check that selected state is preserved [testController.collectionView setContentOffset:CGPointMake(0,testController.collectionView.bounds.size.height)]; [testController.collectionView layoutIfNeeded]; @@ -256,7 +317,7 @@ XCTAssertTrue(node.isSelected == NO, @"Deselecting cell should update node selection."); // check setSelected not called extra times - XCTAssertTrue([(ASTextCellNodeWithSetSelectedCounter *)node setSelectedCounter] == 6, @"setSelected: should not be called on node multiple times."); + XCTAssertTrue([(ASTextCellNodeWithSetSelectedCounter *)node setSelectedCounter] == (setSelectedCount + 1), @"setSelected: should not be called on node multiple times."); } - (void)testTuningParametersWithExplicitRangeMode @@ -443,6 +504,45 @@ } } +- (void)testCellNodeIndexPathConsistency +{ + updateValidationTestPrologue + + // Test with a visible cell + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:2 inSection:0]; + ASCellNode *cell = [cn nodeForItemAtIndexPath:indexPath]; + + // Check if cell's indexPath corresponds to the indexPath being tested + XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == indexPath.item, @"Expected the cell's indexPath to be the same as the indexPath being tested."); + + // Remove an item prior to the cell's indexPath from the same section and check for indexPath consistency + --del->_itemCounts[indexPath.section]; + [cn deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:indexPath.section]]]; + XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == (indexPath.item - 1), @"Expected the cell's indexPath to be updated once a cell with a lower index is deleted."); + + // Remove the section that includes the indexPath and check if the cell's indexPath is now nil + del->_itemCounts.erase(del->_itemCounts.begin()); + [cn deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]]; + XCTAssertNil(cell.indexPath, @"Expected the cell's indexPath to be nil once the section that contains the node is deleted."); + + // Run the same tests but with a non-displayed cell + indexPath = [NSIndexPath indexPathForItem:2 inSection:(del->_itemCounts.size() - 1)]; + cell = [cn nodeForItemAtIndexPath:indexPath]; + + // Check if cell's indexPath corresponds to the indexPath being tested + XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == indexPath.item, @"Expected the cell's indexPath to be the same as the indexPath in question."); + + // Remove an item prior to the cell's indexPath from the same section and check for indexPath consistency + --del->_itemCounts[indexPath.section]; + [cn deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:indexPath.section]]]; + XCTAssertTrue(cell.indexPath.section == indexPath.section && cell.indexPath.item == (indexPath.item - 1), @"Expected the cell's indexPath to be updated once a cell with a lower index is deleted."); + + // Remove the section that includes the indexPath and check if the cell's indexPath is now nil + del->_itemCounts.pop_back(); + [cn deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section]]; + XCTAssertNil(cell.indexPath, @"Expected the cell's indexPath to be nil once the section that contains the node is deleted."); +} + /** * https://github.com/facebook/AsyncDisplayKit/issues/2011 * @@ -462,12 +562,12 @@ [[[dataSource stub] andDo:^(NSInvocation *invocation) { __autoreleasing ASCellNode *suppNode = [[ASCellNode alloc] init]; int thisNodeIdx = nodeIdx++; - suppNode.name = [NSString stringWithFormat:@"Cell #%d", thisNodeIdx]; + suppNode.debugName = [NSString stringWithFormat:@"Cell #%d", thisNodeIdx]; [keepaliveNodes addObject:suppNode]; ASDisplayNode *layerBacked = [[ASDisplayNode alloc] init]; layerBacked.layerBacked = YES; - layerBacked.name = [NSString stringWithFormat:@"Subnode #%d", thisNodeIdx]; + layerBacked.debugName = [NSString stringWithFormat:@"Subnode #%d", thisNodeIdx]; [suppNode addSubnode:layerBacked]; [invocation setReturnValue:&suppNode]; }] collectionNode:cn nodeForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:OCMOCK_ANY]; diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m index 7d8c3f6d2a..7b629e129c 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m @@ -55,10 +55,10 @@ static dispatch_block_t modifyMethodByAddingPrologueBlockAndReturnCleanupBlock(C @end // Conveniences for making nodes named a certain way -#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n +#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.debugName = @#n #define DeclareViewNamed(v) \ ASDisplayNode *node_##v = [[ASDisplayNode alloc] init]; \ - node_##v.name = @#v; \ + node_##v.debugName = @#v; \ UIView *v = node_##v.view; @implementation ASDisplayNodeAppearanceTests diff --git a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m index 356de3ad87..6eac4cd6c0 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m @@ -59,11 +59,21 @@ - (void)testInitialNodeInsertionWithOrdering { + static CGSize kSize = {100, 100}; + ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; ASDisplayNode *node2 = [[ASDisplayNode alloc] init]; ASDisplayNode *node3 = [[ASDisplayNode alloc] init]; ASDisplayNode *node4 = [[ASDisplayNode alloc] init]; ASDisplayNode *node5 = [[ASDisplayNode alloc] init]; + + + // As we will involve a stack spec we have to give the nodes an intrinsic content size + node1.style.preferredSize = kSize; + node2.style.preferredSize = kSize; + node3.style.preferredSize = kSize; + node4.style.preferredSize = kSize; + node5.style.preferredSize = kSize; ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; node.automaticallyManagesSubnodes = YES; @@ -88,10 +98,17 @@ - (void)testCalculatedLayoutHierarchyTransitions { + static CGSize kSize = {100, 100}; + ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; ASDisplayNode *node2 = [[ASDisplayNode alloc] init]; ASDisplayNode *node3 = [[ASDisplayNode alloc] init]; + // As we will involve a stack spec we have to give the nodes an intrinsic content size + node1.style.preferredSize = kSize; + node2.style.preferredSize = kSize; + node3.style.preferredSize = kSize; + ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; node.automaticallyManagesSubnodes = YES; node.layoutSpecBlock = ^(ASDisplayNode *weakNode, ASSizeRange constrainedSize){ @@ -118,20 +135,21 @@ XCTAssertEqual(node.subnodes[2], node2); } -- (void)testLayoutTransitionWillThrowForManualSubnodeManagement -{ - ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; - node1.name = @"node1"; - - ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; - node.automaticallyManagesSubnodes = YES; - node.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode *weakNode, ASSizeRange constrainedSize){ - return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[node1]]; - }; - - XCTAssertNoThrow([node layoutThatFits:ASSizeRangeMake(CGSizeZero)]); - XCTAssertThrows([node1 removeFromSupernode]); -} +// Disable test for now as we disabled the assertion +//- (void)testLayoutTransitionWillThrowForManualSubnodeManagement +//{ +// ASDisplayNode *node1 = [[ASDisplayNode alloc] init]; +// node1.name = @"node1"; +// +// ASSpecTestDisplayNode *node = [[ASSpecTestDisplayNode alloc] init]; +// node.automaticallyManagesSubnodes = YES; +// node.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode *weakNode, ASSizeRange constrainedSize){ +// return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[node1]]; +// }; +// +// XCTAssertNoThrow([node layoutThatFits:ASSizeRangeMake(CGSizeZero)]); +// XCTAssertThrows([node1 removeFromSupernode]); +//} - (void)testLayoutTransitionMeasurementCompletionBlockIsCalledOnMainThread { diff --git a/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm b/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm index 1862064fc3..161b71cca8 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm +++ b/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm @@ -102,4 +102,29 @@ XCTAssertEqual(numberOfLayoutSpecThatFitsCalls, 1, @"Should not remeasure with same bounds"); } +- (void)testThatLayoutWithInvalidSizeCausesException +{ + ASDisplayNode *displayNode = [[ASDisplayNode alloc] init]; + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + node.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode *node, ASSizeRange constrainedSize) { + return [ASWrapperLayoutSpec wrapperWithLayoutElement:displayNode]; + }; + + XCTAssertThrows([node layoutThatFits:ASSizeRangeMake(CGSizeMake(0, FLT_MAX))]); + + // This dance is necessary as we would assert in case we create an ASDimension that is not real numbers + ASDimension width = displayNode.style.width; + width.value = INFINITY; + displayNode.style.width = width; + XCTAssertThrows([node layoutThatFits:ASSizeRangeMake(CGSizeMake(0, 0), CGSizeMake(INFINITY, INFINITY))]); +} + +- (void)testThatLayoutCreatedWithInvalidSizeCausesException +{ + ASDisplayNode *displayNode = [[ASDisplayNode alloc] init]; + XCTAssertThrows([ASLayout layoutWithLayoutElement:displayNode size:CGSizeMake(FLT_MAX, FLT_MAX)]); + XCTAssertThrows([ASLayout layoutWithLayoutElement:displayNode size:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]); + XCTAssertThrows([ASLayout layoutWithLayoutElement:displayNode size:CGSizeMake(INFINITY, INFINITY)]); +} + @end diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 49452caaf8..47fed66bba 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -21,29 +21,31 @@ #import "UIView+ASConvenience.h" #import "ASCellNode.h" #import "ASImageNode.h" +#import "ASOverlayLayoutSpec.h" +#import "ASInsetLayoutSpec.h" // Conveniences for making nodes named a certain way -#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.name = @#n +#define DeclareNodeNamed(n) ASDisplayNode *n = [[ASDisplayNode alloc] init]; n.debugName = @#n #define DeclareViewNamed(v) \ ASDisplayNode *node_##v = [[ASDisplayNode alloc] init]; \ - node_##v.name = @#v; \ + node_##v.debugName = @#v; \ UIView *v = node_##v.view; #define DeclareLayerNamed(l) \ ASDisplayNode *node_##l = [[ASDisplayNode alloc] init]; \ - node_##l.name = @#l; \ + node_##l.debugName = @#l; \ node_##l.layerBacked = YES; \ CALayer *l = node_##l.layer; static NSString *orderStringFromSublayers(CALayer *l) { - return [[[l.sublayers valueForKey:@"asyncdisplaykit_node"] valueForKey:@"name"] componentsJoinedByString:@","]; + return [[[l.sublayers valueForKey:@"asyncdisplaykit_node"] valueForKey:@"debugName"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubviews(UIView *v) { - return [[[v.subviews valueForKey:@"asyncdisplaykit_node"] valueForKey:@"name"] componentsJoinedByString:@","]; + return [[[v.subviews valueForKey:@"asyncdisplaykit_node"] valueForKey:@"debugName"] componentsJoinedByString:@","]; } static NSString *orderStringFromSubnodes(ASDisplayNode *n) { - return [[n.subnodes valueForKey:@"name"] componentsJoinedByString:@","]; + return [[n.subnodes valueForKey:@"debugName"] componentsJoinedByString:@","]; } // Asserts subnode, subview, sublayer order match what you provide here @@ -58,17 +60,17 @@ if (loaded) {\ #define XCTAssertNodesHaveParent(parent, nodes ...) \ for (ASDisplayNode *n in @[ nodes ]) {\ - XCTAssertEqualObjects(parent, n.supernode, @"%@ has the wrong parent", n.name);\ + XCTAssertEqualObjects(parent, n.supernode, @"%@ has the wrong parent", n.debugName);\ } #define XCTAssertNodesLoaded(nodes ...) \ for (ASDisplayNode *n in @[ nodes ]) {\ - XCTAssertTrue(n.nodeLoaded, @"%@ should be loaded", n.name);\ + XCTAssertTrue(n.nodeLoaded, @"%@ should be loaded", n.debugName);\ } #define XCTAssertNodesNotLoaded(nodes ...) \ for (ASDisplayNode *n in @[ nodes ]) {\ - XCTAssertFalse(n.nodeLoaded, @"%@ should not be loaded", n.name);\ + XCTAssertFalse(n.nodeLoaded, @"%@ should not be loaded", n.debugName);\ } @@ -328,7 +330,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ XCTAssertEqual(NO, node.displaySuspended, @"default displaySuspended broken %@", hasLoadedView); XCTAssertEqual(YES, node.displaysAsynchronously, @"default displaysAsynchronously broken %@", hasLoadedView); XCTAssertEqual(NO, node.asyncdisplaykit_asyncTransactionContainer, @"default asyncdisplaykit_asyncTransactionContainer broken %@", hasLoadedView); - XCTAssertEqualObjects(nil, node.name, @"default name broken %@", hasLoadedView); + XCTAssertEqualObjects(nil, node.debugName, @"default name broken %@", hasLoadedView); XCTAssertEqual(NO, node.isAccessibilityElement, @"default isAccessibilityElement is broken %@", hasLoadedView); XCTAssertEqual((id)nil, node.accessibilityLabel, @"default accessibilityLabel is broken %@", hasLoadedView); @@ -426,7 +428,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ XCTAssertEqual(YES, node.asyncdisplaykit_asyncTransactionContainer, @"asyncTransactionContainer broken %@", hasLoadedView); XCTAssertEqual(NO, node.userInteractionEnabled, @"userInteractionEnabled broken %@", hasLoadedView); XCTAssertEqual((BOOL)!isLayerBacked, node.exclusiveTouch, @"exclusiveTouch broken %@", hasLoadedView); - XCTAssertEqualObjects(@"quack like a duck", node.name, @"name broken %@", hasLoadedView); + XCTAssertEqualObjects(@"quack like a duck", node.debugName, @"debugName broken %@", hasLoadedView); XCTAssertEqual(YES, node.isAccessibilityElement, @"accessibilityElement broken %@", hasLoadedView); XCTAssertEqualObjects(@"Ship love", node.accessibilityLabel, @"accessibilityLabel broken %@", hasLoadedView); @@ -484,7 +486,7 @@ for (ASDisplayNode *n in @[ nodes ]) {\ node.displaysAsynchronously = NO; node.asyncdisplaykit_asyncTransactionContainer = YES; node.userInteractionEnabled = NO; - node.name = @"quack like a duck"; + node.debugName = @"quack like a duck"; node.isAccessibilityElement = YES; node.accessibilityLabel = @"Ship love"; @@ -1849,14 +1851,14 @@ static bool stringContainsPointer(NSString *description, id p) { ASDisplayNode *node = [[ASDisplayNode alloc] init]; node.layerBacked = isLayerBacked; - XCTAssertFalse([node.description rangeOfString:@"name"].location != NSNotFound, @"Shouldn't reference 'name' in description"); - node.name = @"big troll eater name"; + XCTAssertFalse([node.description rangeOfString:@"debugName"].location != NSNotFound, @"Shouldn't reference 'debugName' in description"); + node.debugName = @"big troll eater name"; - XCTAssertTrue([node.description rangeOfString:node.name].location != NSNotFound, @"Name didn't end up in description"); - XCTAssertTrue([node.description rangeOfString:@"name"].location != NSNotFound, @"Shouldn't reference 'name' in description"); + XCTAssertFalse([node.description rangeOfString:node.debugName].location == NSNotFound, @"debugName didn't end up in description"); + XCTAssertFalse([node.description rangeOfString:@"debugName"].location == NSNotFound, @"Shouldn't reference 'debugName' in description"); [node layer]; - XCTAssertTrue([node.description rangeOfString:node.name].location != NSNotFound, @"Name didn't end up in description"); - XCTAssertTrue([node.description rangeOfString:@"name"].location != NSNotFound, @"Shouldn't reference 'name' in description"); + XCTAssertFalse([node.description rangeOfString:node.debugName].location == NSNotFound, @"debugName didn't end up in description"); + XCTAssertFalse([node.description rangeOfString:@"debugName"].location == NSNotFound, @"Shouldn't reference 'debugName' in description"); } - (void)testNameInDescriptionLayer @@ -1984,12 +1986,12 @@ static bool stringContainsPointer(NSString *description, id p) { { NS_VALID_UNTIL_END_OF_SCOPE ASDisplayNode *node = [[ASDisplayNode alloc] init]; nodeView = node.view; - node.name = @"Node"; + node.debugName = @"Node"; NS_VALID_UNTIL_END_OF_SCOPE ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; subnode.layerBacked = YES; [node addSubnode:subnode]; - subnode.name = @"Subnode"; + subnode.debugName = @"Subnode"; [window addSubview:nodeView]; } @@ -2021,11 +2023,11 @@ static bool stringContainsPointer(NSString *description, id p) { - (void)testThatSubnodeGetsInterfaceStateSetIfRasterized { ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init]; - node.name = @"Node"; + node.debugName = @"Node"; node.shouldRasterizeDescendants = YES; ASTestDisplayNode *subnode = [[ASTestDisplayNode alloc] init]; - subnode.name = @"Subnode"; + subnode.debugName = @"Subnode"; [node addSubnode:subnode]; [node view]; // Node needs to be loaded @@ -2139,4 +2141,14 @@ static bool stringContainsPointer(NSString *description, id p) { } } +- (void)testThatHavingTheSameNodeTwiceInALayoutSpecCausesExceptionOnLayoutCalculation +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + ASDisplayNode *subnode = [[ASDisplayNode alloc] init]; + node.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode *node, ASSizeRange constrainedSize) { + return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:subnode] overlay:subnode]; + }; + XCTAssertThrowsSpecificNamed([node calculateLayoutThatFits:ASSizeRangeMake(CGSizeMake(100, 100))], NSException, NSInternalInconsistencyException); +} + @end diff --git a/AsyncDisplayKitTests/ASLayoutElementStyleTests.m b/AsyncDisplayKitTests/ASLayoutElementStyleTests.m index 6f4caabebb..5a7e7cbe8b 100644 --- a/AsyncDisplayKitTests/ASLayoutElementStyleTests.m +++ b/AsyncDisplayKitTests/ASLayoutElementStyleTests.m @@ -8,7 +8,7 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import +#import "ASXCTExtensions.h" #import "ASLayoutElement.h" #pragma mark - ASLayoutElementStyleTestsDelegate @@ -58,9 +58,12 @@ { ASLayoutElementStyle *style = [ASLayoutElementStyle new]; + ASXCTAssertEqualSizes(style.preferredSize, CGSizeZero); + CGSize size = CGSizeMake(100, 100); style.preferredSize = size; + ASXCTAssertEqualSizes(style.preferredSize, size); XCTAssertTrue(ASDimensionEqualToDimension(style.width, ASDimensionMakeWithPoints(size.width))); XCTAssertTrue(ASDimensionEqualToDimension(style.height, ASDimensionMakeWithPoints(size.height))); @@ -73,6 +76,19 @@ XCTAssertTrue(ASDimensionEqualToDimension(style.maxHeight, ASDimensionMakeWithPoints(size.height))); } +- (void)testReadingInvalidSizeForPreferredSize +{ + ASLayoutElementStyle *style = [ASLayoutElementStyle new]; + + XCTAssertNoThrow(style.preferredSize); + + style.width = ASDimensionMake(ASDimensionUnitFraction, 0.5); + XCTAssertThrows(style.preferredSize); + + style.preferredSize = CGSizeMake(100, 100); + XCTAssertNoThrow(style.preferredSize); +} + - (void)testSettingSizeViaLayoutSize { ASLayoutElementStyle *style = [ASLayoutElementStyle new]; diff --git a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm index 3fa2df4d39..d507f4b77f 100644 --- a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm @@ -21,6 +21,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}}; @implementation ASRelativeLayoutSpecSnapshotTests +#pragma mark - XCTestCase + +- (void)setUp +{ + [super setUp]; + + self.recordMode = NO; +} + + - (void)testWithOptions { [self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionStart]; @@ -29,7 +39,8 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}}; } -- (void)testAllVerticalPositionsForHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition { +- (void)testAllVerticalPositionsForHorizontalPosition:(ASRelativeLayoutSpecPosition)horizontalPosition +{ [self testWithHorizontalPosition:horizontalPosition verticalPosition:ASRelativeLayoutSpecPositionStart sizingOptions:{}]; [self testWithHorizontalPosition:horizontalPosition verticalPosition:ASRelativeLayoutSpecPositionCenter sizingOptions:{}]; [self testWithHorizontalPosition:horizontalPosition verticalPosition:ASRelativeLayoutSpecPositionEnd sizingOptions:{}]; @@ -76,19 +87,19 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}}; static NSString *suffixForPositionOptions(ASRelativeLayoutSpecPosition horizontalPosition, ASRelativeLayoutSpecPosition verticalPosition, - ASRelativeLayoutSpecSizingOption sizingOptions) + ASRelativeLayoutSpecSizingOption sizingOptions) { NSMutableString *suffix = [NSMutableString string]; - if ((horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0) { + if (horizontalPosition == ASRelativeLayoutSpecPositionCenter) { [suffix appendString:@"CenterX"]; - } else if ((horizontalPosition & ASRelativeLayoutSpecPositionEnd) != 0) { + } else if (horizontalPosition == ASRelativeLayoutSpecPositionEnd) { [suffix appendString:@"EndX"]; } - if ((verticalPosition & ASRelativeLayoutSpecPositionCenter) != 0) { + if (verticalPosition == ASRelativeLayoutSpecPositionCenter) { [suffix appendString:@"CenterY"]; - } else if ((verticalPosition & ASRelativeLayoutSpecPositionEnd) != 0) { + } else if (verticalPosition == ASRelativeLayoutSpecPositionEnd) { [suffix appendString:@"EndY"]; } @@ -122,8 +133,8 @@ static NSString *suffixForPositionOptions(ASRelativeLayoutSpecPosition horizonta ASRelativeLayoutSpec *layoutSpec = [ASRelativeLayoutSpec - relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart - verticalPosition:ASRelativeLayoutSpecPositionStart + relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionNone + verticalPosition:ASRelativeLayoutSpecPositionNone sizingOption:{} child:childSpec]; diff --git a/AsyncDisplayKitTests/ASSnapshotTestCase.h b/AsyncDisplayKitTests/ASSnapshotTestCase.h index 977e267d17..c89fc79af4 100644 --- a/AsyncDisplayKitTests/ASSnapshotTestCase.h +++ b/AsyncDisplayKitTests/ASSnapshotTestCase.h @@ -29,6 +29,9 @@ NSOrderedSet *ASSnapshotTestCaseDefaultSuffixes(void); #define ASSnapshotVerifyLayer(layer__, identifier__) \ FBSnapshotVerifyLayerWithOptions(layer__, identifier__, ASSnapshotTestCaseDefaultSuffixes(), 0); +#define ASSnapshotVerifyView(view__, identifier__) \ + FBSnapshotVerifyViewWithOptions(view__, identifier__, ASSnapshotTestCaseDefaultSuffixes(), 0); + @interface ASSnapshotTestCase : FBSnapshotTestCase /** diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index f57e1d0220..752a9f94d4 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -17,6 +17,7 @@ #import "ASCellNode.h" #import "ASTableNode.h" #import "ASTableView+Undeprecated.h" +#import #define NumberOfSections 10 #define NumberOfRowsPerSection 20 @@ -36,6 +37,13 @@ @end +@interface UITableView (Testing) +// This will start recording all editing calls to UITableView +// into the provided array. +// Make sure to call [UITableView deswizzleInstanceMethods] to reset this. ++ (void)as_recordEditingCallsIntoArray:(NSMutableArray *)selectors; +@end + @interface ASTestTableView : ASTableView @property (nonatomic, copy) void (^willDeallocBlock)(ASTableView *tableView); @end @@ -256,7 +264,7 @@ return indexPaths; } -- (void)testReloadData +- (void)DISABLED_testReloadData { // Keep the viewport moderately sized so that new cells are loaded on scrolling ASTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectMake(0, 0, 100, 500) @@ -544,4 +552,116 @@ XCTAssert([node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]); } +- (void)testThatInitialDataLoadHappensInOneShot +{ + NSMutableArray *selectors = [NSMutableArray array]; + ASTableNode *node = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + node.frame = CGRectMake(0, 0, 100, 100); + + node.dataSource = dataSource; + node.delegate = dataSource; + + [UITableView as_recordEditingCallsIntoArray:selectors]; + XCTAssertGreaterThan(node.numberOfSections, 0); + [node waitUntilAllUpdatesAreCommitted]; + XCTAssertGreaterThan(node.view.numberOfSections, 0); + + // Assert that the beginning of the call pattern is correct. + // There is currently noise that comes after that we will allow for this test. + NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)), + NSStringFromSelector(@selector(insertSections:withRowAnimation:)), + NSStringFromSelector(@selector(insertRowsAtIndexPaths:withRowAnimation:))]; + NSArray *firstSelectors = [selectors subarrayWithRange:NSMakeRange(0, expectedSelectors.count)]; + XCTAssertEqualObjects(firstSelectors, expectedSelectors); + + [UITableView deswizzleAllInstanceMethods]; +} + +- (void)testThatReloadDataHappensInOneShot +{ + NSMutableArray *selectors = [NSMutableArray array]; + ASTableNode *node = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + + ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; + node.frame = CGRectMake(0, 0, 100, 100); + + node.dataSource = dataSource; + node.delegate = dataSource; + + // Load initial data. + XCTAssertGreaterThan(node.numberOfSections, 0); + [node waitUntilAllUpdatesAreCommitted]; + XCTAssertGreaterThan(node.view.numberOfSections, 0); + + // Reload data. + [UITableView as_recordEditingCallsIntoArray:selectors]; + [node reloadData]; + [node waitUntilAllUpdatesAreCommitted]; + + // Assert that the beginning of the call pattern is correct. + // There is currently noise that comes after that we will allow for this test. + NSArray *expectedSelectors = @[NSStringFromSelector(@selector(reloadData)), + NSStringFromSelector(@selector(beginUpdates)), + NSStringFromSelector(@selector(deleteSections:withRowAnimation:)), + NSStringFromSelector(@selector(insertSections:withRowAnimation:)), + NSStringFromSelector(@selector(endUpdates)), + NSStringFromSelector(@selector(insertRowsAtIndexPaths:withRowAnimation:))]; + NSArray *firstSelectors = [selectors subarrayWithRange:NSMakeRange(0, expectedSelectors.count)]; + XCTAssertEqualObjects(firstSelectors, expectedSelectors); + + [UITableView deswizzleAllInstanceMethods]; +} + +@end + +@implementation UITableView (Testing) + ++ (void)as_recordEditingCallsIntoArray:(NSMutableArray *)selectors +{ + [UITableView swizzleInstanceMethod:@selector(reloadData) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *) { + JGOriginalImplementation(void); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(beginUpdates) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *) { + JGOriginalImplementation(void); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(endUpdates) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *) { + JGOriginalImplementation(void); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(insertRowsAtIndexPaths:withRowAnimation:) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *, NSArray *indexPaths, UITableViewRowAnimation anim) { + JGOriginalImplementation(void, indexPaths, anim); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(deleteRowsAtIndexPaths:withRowAnimation:) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *, NSArray *indexPaths, UITableViewRowAnimation anim) { + JGOriginalImplementation(void, indexPaths, anim); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(insertSections:withRowAnimation:) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *, NSIndexSet *indexes, UITableViewRowAnimation anim) { + JGOriginalImplementation(void, indexes, anim); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; + [UITableView swizzleInstanceMethod:@selector(deleteSections:withRowAnimation:) withReplacement:JGMethodReplacementProviderBlock { + return JGMethodReplacement(void, UITableView *, NSIndexSet *indexes, UITableViewRowAnimation anim) { + JGOriginalImplementation(void, indexes, anim); + [selectors addObject:NSStringFromSelector(_cmd)]; + }; + }]; +} + @end diff --git a/AsyncDisplayKitTests/ASTextNodePerformanceTests.m b/AsyncDisplayKitTests/ASTextNodePerformanceTests.m index 9ed8f1ea0c..d3a0fa50e8 100644 --- a/AsyncDisplayKitTests/ASTextNodePerformanceTests.m +++ b/AsyncDisplayKitTests/ASTextNodePerformanceTests.m @@ -108,7 +108,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext"; ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize); ASXCTAssertEqualSizes(uiKitSize, asdkSize); - ASXCTAssertRelativePerformanceInRange(ctx, kTestCaseASDK, 0.2, 0.5); + ASXCTAssertRelativePerformanceInRange(ctx, kTestCaseASDK, 0.5, 0.9); } - (void)testPerformance_OneParagraphLatinWithTruncation diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m index 895f1841af..08b953374d 100644 --- a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m @@ -72,7 +72,24 @@ textNode.highlightRange = NSMakeRange(0, textNode.attributedText.length); [ASSnapshotTestCase hackilySynchronouslyRecursivelyRenderNode:textNode]; - ASSnapshotVerifyLayer(backgroundView.layer, nil); + ASSnapshotVerifyView(backgroundView, nil); +} + +- (void)testThatFastPathTruncationWorks +{ + ASTextNode *textNode = [[ASTextNode alloc] init]; + textNode.attributedText = [[NSAttributedString alloc] initWithString:@"Quality is Important" attributes:@{ NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont italicSystemFontOfSize:24] }]; + [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 50))]; + ASSnapshotVerifyNode(textNode, nil); +} + +- (void)testThatSlowPathTruncationWorks +{ + ASTextNode *textNode = [[ASTextNode alloc] init]; + textNode.attributedText = [[NSAttributedString alloc] initWithString:@"Quality is Important" attributes:@{ NSForegroundColorAttributeName: [UIColor blueColor], NSFontAttributeName: [UIFont italicSystemFontOfSize:24] }]; + [textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 50))]; + textNode.exclusionPaths = @[ [UIBezierPath bezierPath] ]; + ASSnapshotVerifyNode(textNode, nil); } @end diff --git a/AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm new file mode 100644 index 0000000000..d28b8b0fe2 --- /dev/null +++ b/AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm @@ -0,0 +1,60 @@ +// +// ASWrapperSpecSnapshotTests.mm +// AsyncDisplayKit +// +// 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 "ASLayoutSpecSnapshotTestsHelper.h" +#import "ASBackgroundLayoutSpec.h" + +@interface ASWrapperSpecSnapshotTests : ASLayoutSpecSnapshotTestCase +@end + +@implementation ASWrapperSpecSnapshotTests + +- (void)setUp +{ + [super setUp]; + + self.recordMode = NO; +} + +- (void)testWrapperSpecWithOneElementShouldSizeToElement +{ + ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); + + ASSizeRange sizeRange = ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)); + [self testWithChildren:@[child] sizeRange:sizeRange identifier:nil]; +} + +- (void)testWrapperSpecWithMultipleElementsShouldSizeToLargestElement +{ + ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); + ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {100, 100}); + + ASSizeRange sizeRange = ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)); + [self testWithChildren:@[secondChild, firstChild] sizeRange:sizeRange identifier:nil]; +} + +- (void)testWithChildren:(NSArray *)children sizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier +{ + ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]); + + NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children]; + [subnodes insertObject:backgroundNode atIndex:0]; + + ASLayoutSpec *layoutSpec = + [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild: + [ASWrapperLayoutSpec + wrapperWithLayoutElements:children] + background:backgroundNode]; + + [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; +} + +@end diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@3x.png deleted file mode 100644 index 78fafd3fd6..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@3x.png deleted file mode 100644 index 2023ff6ad4..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@3x.png deleted file mode 100644 index d34850d927..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@3x.png deleted file mode 100644 index e1f26129ef..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@3x.png deleted file mode 100644 index e9bb3f4098..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@3x.png deleted file mode 100644 index 3fb6feee42..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@3x.png deleted file mode 100644 index c2bf62b078..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@3x.png deleted file mode 100644 index f1d9c2e52f..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@3x.png deleted file mode 100644 index 8093c89761..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@3x.png deleted file mode 100644 index 7e824c2073..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@3x.png deleted file mode 100644 index c2bf62b078..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@3x.png deleted file mode 100644 index 6796058c28..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@3x.png deleted file mode 100644 index d4a15f979b..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@3x.png deleted file mode 100644 index 802dbc7bca..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testRenderLogoSquare@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testRenderLogoSquare@3x.png deleted file mode 100644 index 97119f1dc2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testRenderLogoSquare@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@3x.png deleted file mode 100644 index 94b3687343..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@3x.png deleted file mode 100644 index ff3ea416c7..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@3x.png deleted file mode 100644 index 463944922b..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@3x.png deleted file mode 100644 index 4367dcf7de..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@3x.png deleted file mode 100644 index fe07dde6ca..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@3x.png deleted file mode 100644 index 639e698109..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@3x.png deleted file mode 100644 index b2c041116d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@3x.png deleted file mode 100644 index 88d16405d2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@3x.png deleted file mode 100644 index 89c5baf629..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@3x.png deleted file mode 100644 index 98a3f5faab..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@3x.png deleted file mode 100644 index 79c1dd46be..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@3x.png deleted file mode 100644 index 651468da4d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@3x.png deleted file mode 100644 index 9838114469..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@3x.png deleted file mode 100644 index b71a6ad440..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@3x.png deleted file mode 100644 index 4d863686b8..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@3x.png deleted file mode 100644 index f517f65d5c..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@3x.png deleted file mode 100644 index 2dc72883af..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@3x.png deleted file mode 100644 index 2e94b4f983..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@3x.png deleted file mode 100644 index a23456fa40..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@3x.png deleted file mode 100644 index 9def6b5706..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@3x.png deleted file mode 100644 index b4fbf19ec1..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@3x.png deleted file mode 100644 index 08d9a49f40..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@3x.png deleted file mode 100644 index 9b5689fa01..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@3x.png deleted file mode 100644 index 1202436035..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@3x.png deleted file mode 100644 index 38a85d9317..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@3x.png deleted file mode 100644 index abe3b38376..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@3x.png deleted file mode 100644 index 54b8339994..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@3x.png deleted file mode 100644 index 7021cf7fc6..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@3x.png deleted file mode 100644 index 2c9f2facff..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@3x.png deleted file mode 100644 index 3fd9a4e1bb..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@3x.png deleted file mode 100644 index f68b2bd9fe..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@3x.png deleted file mode 100644 index f517f65d5c..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@3x.png deleted file mode 100644 index 3004b336f2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@3x.png deleted file mode 100644 index a645e3dbd4..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@3x.png deleted file mode 100644 index 8a01a112f5..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@3x.png deleted file mode 100644 index 4367dcf7de..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@3x.png deleted file mode 100644 index 558fd84768..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@3x.png deleted file mode 100644 index 10f466575a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@3x.png deleted file mode 100644 index b2c041116d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@3x.png deleted file mode 100644 index 88d16405d2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@3x.png deleted file mode 100644 index eb9cf61acb..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@3x.png deleted file mode 100644 index 98a3f5faab..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@3x.png deleted file mode 100644 index 232924d3dd..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@3x.png deleted file mode 100644 index 651468da4d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@3x.png deleted file mode 100644 index 9838114469..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@3x.png deleted file mode 100644 index b71a6ad440..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@3x.png deleted file mode 100644 index 4d863686b8..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@3x.png deleted file mode 100644 index f517f65d5c..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@3x.png deleted file mode 100644 index e9bb3f4098..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@3x.png deleted file mode 100644 index 099f811e66..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@3x.png deleted file mode 100644 index c8c49a5841..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@3x.png deleted file mode 100644 index 5fd4b9237d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@3x.png deleted file mode 100644 index 0121889335..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png new file mode 100644 index 0000000000..02717f8fdb Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@3x.png deleted file mode 100644 index 3fb6feee42..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png new file mode 100644 index 0000000000..1192d72e8d Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@3x.png deleted file mode 100644 index c2bf62b078..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png new file mode 100644 index 0000000000..e2c7e8266b Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@3x.png deleted file mode 100644 index f1d9c2e52f..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png new file mode 100644 index 0000000000..311ef9ed32 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@3x.png deleted file mode 100644 index 8093c89761..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png new file mode 100644 index 0000000000..385fc3e817 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@3x.png deleted file mode 100644 index d9dab946b4..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png new file mode 100644 index 0000000000..94db1b131f Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@3x.png deleted file mode 100644 index 7e824c2073..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png new file mode 100644 index 0000000000..e464619571 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@3x.png deleted file mode 100644 index 9064f33704..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png new file mode 100644 index 0000000000..04ee2d6115 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@3x.png deleted file mode 100644 index e634d8e090..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png new file mode 100644 index 0000000000..3d9d16b5d2 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@3x.png deleted file mode 100644 index 448ad0d27d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png new file mode 100644 index 0000000000..8ba7270db2 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@3x.png deleted file mode 100644 index 9f88fcc02d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png new file mode 100644 index 0000000000..1192d72e8d Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@3x.png deleted file mode 100644 index c2bf62b078..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png new file mode 100644 index 0000000000..467bb6fa8c Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@3x.png deleted file mode 100644 index 802dbc7bca..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png new file mode 100644 index 0000000000..8e9c66caa1 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@3x.png deleted file mode 100644 index 6796058c28..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png new file mode 100644 index 0000000000..ddafb41db7 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@3x.png deleted file mode 100644 index d4a15f979b..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png deleted file mode 100644 index 4be0521204..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@3x.png deleted file mode 100644 index 042dfd0498..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@3x.png deleted file mode 100644 index 70b2edb7ba..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@3x.png deleted file mode 100644 index e7de065228..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@3x.png deleted file mode 100644 index f37d2b443c..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png deleted file mode 100644 index 8c021540b4..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png deleted file mode 100644 index 092dc950bf..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@3x.png deleted file mode 100644 index cf50ce8f90..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@3x.png deleted file mode 100644 index d191cfbba2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@3x.png deleted file mode 100644 index 22ce210dca..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@3x.png deleted file mode 100644 index 45700fe8af..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@3x.png deleted file mode 100644 index 98b75fc162..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@3x.png deleted file mode 100644 index 12bfc63001..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png deleted file mode 100644 index 647272839d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@3x.png deleted file mode 100644 index 54ac8f2b5b..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@3x.png deleted file mode 100644 index 4f9ffc33ab..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@3x.png deleted file mode 100644 index dc22edc0aa..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@3x.png deleted file mode 100644 index 312623d230..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png deleted file mode 100644 index 1b82cbb9c4..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@3x.png deleted file mode 100644 index c4db41e310..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@3x.png deleted file mode 100644 index ca92a9bf7f..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@3x.png deleted file mode 100644 index 19e98d65b8..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@3x.png deleted file mode 100644 index 36a78c6fe7..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@3x.png deleted file mode 100644 index ae763d07e3..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@3x.png deleted file mode 100644 index 8ba43f0549..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@3x.png deleted file mode 100644 index 71df1cf208..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@3x.png deleted file mode 100644 index 2d2de74cb9..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@3x.png deleted file mode 100644 index f6249587a0..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@3x.png deleted file mode 100644 index f97ca1ee1f..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@3x.png deleted file mode 100644 index ea5c8e4db7..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@3x.png deleted file mode 100644 index 34be81fa1a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@3x.png deleted file mode 100644 index 1baf4bca46..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@3x.png deleted file mode 100644 index 1cfcea8ae2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@3x.png deleted file mode 100644 index 0ba9ef5226..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@3x.png deleted file mode 100644 index 0ba9ef5226..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@3x.png deleted file mode 100644 index 43440d2045..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@3x.png deleted file mode 100644 index df6aaf6408..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@3x.png deleted file mode 100644 index df6aaf6408..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@3x.png deleted file mode 100644 index bd02203060..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@3x.png deleted file mode 100644 index bd02203060..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@3x.png deleted file mode 100644 index 43440d2045..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@3x.png deleted file mode 100644 index 1cfcea8ae2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png deleted file mode 100644 index 571449ba8a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@3x.png deleted file mode 100644 index 3c6994df44..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png deleted file mode 100644 index c602d38886..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@3x.png deleted file mode 100644 index 74b509f6cc..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@3x.png deleted file mode 100644 index 1efcebc1cc..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@3x.png deleted file mode 100644 index e6b5ab6e49..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@3x.png deleted file mode 100644 index 50fc7d1f8a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@3x.png deleted file mode 100644 index a8d657742e..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@3x.png deleted file mode 100644 index a8d657742e..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@3x.png deleted file mode 100644 index 50fc7d1f8a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@3x.png deleted file mode 100644 index 573dea9c82..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@3x.png deleted file mode 100644 index cb8efb682d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@3x.png deleted file mode 100644 index cb8efb682d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@3x.png deleted file mode 100644 index 573dea9c82..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png deleted file mode 100644 index ee836d5cfb..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@3x.png deleted file mode 100644 index ee836d5cfb..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@3x.png deleted file mode 100644 index bb44b6a4df..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@3x.png deleted file mode 100644 index b5d3bc5e9a..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@3x.png deleted file mode 100644 index f5479576db..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@3x.png deleted file mode 100644 index f89083148f..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@3x.png deleted file mode 100644 index 7c06d73fa3..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@3x.png deleted file mode 100644 index 457062ea1d..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@3x.png deleted file mode 100644 index eea9e51c30..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@3x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@3x.png deleted file mode 100644 index 4a34478cec..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@3x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png new file mode 100644 index 0000000000..d9f01fce31 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png new file mode 100644 index 0000000000..dcae003909 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png new file mode 100644 index 0000000000..e34eef76c7 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png new file mode 100644 index 0000000000..d63856e6ec Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png new file mode 100644 index 0000000000..37b5444efa Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png new file mode 100644 index 0000000000..bf816c2ed4 Binary files /dev/null and b/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png differ diff --git a/Base/ASBaseDefines.h b/Base/ASBaseDefines.h index 265c1544de..8db12be97e 100755 --- a/Base/ASBaseDefines.h +++ b/Base/ASBaseDefines.h @@ -71,7 +71,7 @@ #endif #ifndef ASDISPLAYNODE_WARN_DEPRECATED -# define ASDISPLAYNODE_WARN_DEPRECATED 1 +# define ASDISPLAYNODE_WARN_DEPRECATED 0 #endif #ifndef ASDISPLAYNODE_DEPRECATED @@ -82,6 +82,14 @@ # endif #endif +#ifndef ASDISPLAYNODE_DEPRECATED_MSG +# if ASDISPLAYNODE_GNUC (3, 0) && ASDISPLAYNODE_WARN_DEPRECATED +# define ASDISPLAYNODE_DEPRECATED_MSG(msg) __deprecated_msg(msg) +# else +# define ASDISPLAYNODE_DEPRECATED_MSG(msg) +# endif +#endif + #if defined (__cplusplus) && defined (__GNUC__) # define ASDISPLAYNODE_NOTHROW __attribute__ ((nothrow)) #else diff --git a/Base/ASLog.h b/Base/ASLog.h index 8f0d1e3cfe..9e4a7bebaf 100644 --- a/Base/ASLog.h +++ b/Base/ASLog.h @@ -10,7 +10,6 @@ #pragma once -#import #import "ASAvailability.h" #define ASMultiplexImageNodeLogDebug(...) @@ -19,7 +18,11 @@ #define ASMultiplexImageNodeLogError(...) #define ASMultiplexImageNodeCLogError(...) -#if PROFILE +// Note: `` only exists in Xcode 8 and later. +#if PROFILE && __has_include("") + +#import + // These definitions are required to build the backward-compatible kdebug trace // on the iOS 10 SDK. The kdebug_trace function crashes if run on iOS 9 and earlier. // It's valuable to support trace signposts on iOS 9, because A5 devices don't support iOS 10. diff --git a/Podfile b/Podfile index 9edf80f1e6..d1b954c71b 100644 --- a/Podfile +++ b/Podfile @@ -4,6 +4,6 @@ platform :ios, '7.0' target :'AsyncDisplayKitTests' do pod 'OCMock', '~> 2.2' - # this is identical to FBSnapshotTestCase 2.1.3 except that the deployment was changed from 8.0 -> 7.0 - pod 'FBSnapshotTestCase/Core', :git => "https://github.com/hannahmbanana/ios-snapshot-test-case.git" + pod 'FBSnapshotTestCase/Core', '~> 2.1' + pod 'JGMethodSwizzler', :git => 'https://github.com/JonasGessner/JGMethodSwizzler', :branch => 'master' end diff --git a/examples/ASCollectionView/Sample/ItemNode.m b/examples/ASCollectionView/Sample/ItemNode.m index fedd2ca9d5..2f61c4ff46 100644 --- a/examples/ASCollectionView/Sample/ItemNode.m +++ b/examples/ASCollectionView/Sample/ItemNode.m @@ -22,10 +22,12 @@ - (instancetype)initWithString:(NSString *)string { self = [super init]; + if (self != nil) { self.text = string; [self updateBackgroundColor]; } + return self; } @@ -43,12 +45,14 @@ - (void)setSelected:(BOOL)selected { [super setSelected:selected]; + [self updateBackgroundColor]; } - (void)setHighlighted:(BOOL)highlighted { [super setHighlighted:highlighted]; + [self updateBackgroundColor]; } diff --git a/examples/ASCollectionView/Sample/PresentingViewController.m b/examples/ASCollectionView/Sample/PresentingViewController.m index f191898d82..38a34fc357 100644 --- a/examples/ASCollectionView/Sample/PresentingViewController.m +++ b/examples/ASCollectionView/Sample/PresentingViewController.m @@ -30,7 +30,10 @@ { [super viewDidLoad]; - self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Details" style:UIBarButtonItemStylePlain target:self action:@selector(pushNewViewController)]; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Push Details" + style:UIBarButtonItemStylePlain + target:self + action:@selector(pushNewViewController)]; } - (void)pushNewViewController diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.m b/examples/ASCollectionView/Sample/SupplementaryNode.m index af5977711f..b1b1b905c9 100644 --- a/examples/ASCollectionView/Sample/SupplementaryNode.m +++ b/examples/ASCollectionView/Sample/SupplementaryNode.m @@ -32,12 +32,14 @@ static CGFloat kInsets = 15.0; - (instancetype)initWithText:(NSString *)text { self = [super init]; + if (self != nil) { _textNode = [[ASTextNode alloc] init]; _textNode.attributedText = [[NSAttributedString alloc] initWithString:text attributes:[self textAttributes]]; [self addSubnode:_textNode]; } + return self; } @@ -47,6 +49,7 @@ static CGFloat kInsets = 15.0; center.centeringOptions = ASCenterLayoutSpecCenteringXY; center.child = self.textNode; UIEdgeInsets insets = UIEdgeInsetsMake(kInsets, kInsets, kInsets, kInsets); + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:center]; } diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 302cbf6127..56a1d510ec 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -21,18 +21,17 @@ #import "SupplementaryNode.h" #import "ItemNode.h" -@interface ViewController () -@property (nonatomic, strong) ASCollectionView *collectionView; -@property (nonatomic, strong) NSArray *data; -@end - @interface ViewController () +@property (nonatomic, strong) ASCollectionView *collectionView; +@property (nonatomic, strong) NSArray *data; + @end - @implementation ViewController +#pragma mark - Lifecycle + - (void)dealloc { self.collectionView.asyncDataSource = nil; @@ -52,20 +51,24 @@ // This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView. // This functionality & example project remains for users who insist on using ASCollectionView. self.collectionView = [[ASCollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout]; - self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.collectionView.asyncDataSource = self; self.collectionView.asyncDelegate = self; + + self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.collectionView.backgroundColor = [UIColor whiteColor]; // This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView. // This functionality & example project remains for users who insist on using ASCollectionView. [self.collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; [self.collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; + [self.view addSubview:self.collectionView]; #if !SIMULATE_WEB_RESPONSE self.navigationItem.leftItemsSupplementBackButton = YES; - self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(reloadTapped)]; + self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh + target:self + action:@selector(reloadTapped)]; #endif #if SIMULATE_WEB_RESPONSE @@ -94,6 +97,8 @@ #endif } +#pragma mark - Button Actions + - (void)reloadTapped { // This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView. @@ -101,8 +106,7 @@ [self.collectionView reloadData]; } -#pragma mark - -#pragma mark ASCollectionView data source. +#pragma mark - ASCollectionView Data Source - (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath; { diff --git a/examples/ASDKgram/Sample/CommentsNode.m b/examples/ASDKgram/Sample/CommentsNode.m index 56e270faa5..67859bd37f 100644 --- a/examples/ASDKgram/Sample/CommentsNode.m +++ b/examples/ASDKgram/Sample/CommentsNode.m @@ -43,11 +43,12 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec]; - verticalStack.spacing = INTER_COMMENT_SPACING; - [verticalStack setChildren:_commentNodes]; - - return verticalStack; + return [ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:INTER_COMMENT_SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:[_commentNodes copy]]; } #pragma mark - Instance Methods diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index db5f8b55b3..176cf4b608 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -91,7 +91,8 @@ _photoDescriptionLabel.maximumNumberOfLines = 3; _photoCommentsView = [[CommentsNode alloc] init]; - _photoCommentsView.shouldRasterizeDescendants = YES; + // For now disable shouldRasterizeDescendants as it will throw an assertion: 'Node should always be marked invisible before deallocating. ...' + //_photoCommentsView.shouldRasterizeDescendants = YES; // instead of adding everything addSubnode: self.automaticallyManagesSubnodes = YES; @@ -112,59 +113,89 @@ - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - // username / photo location header vertical stack - _photoLocationLabel.style.flexShrink = 1.0; - _userNameLabel.style.flexShrink = 1.0; - - ASStackLayoutSpec *headerSubStack = [ASStackLayoutSpec verticalStackLayoutSpec]; - headerSubStack.style.flexShrink = 1.0; - if (_photoLocationLabel.attributedText) { - [headerSubStack setChildren:@[_userNameLabel, _photoLocationLabel]]; - } else { - [headerSubStack setChildren:@[_userNameLabel]]; - } - - // header stack - - // constrain avatar image frame size - _userAvatarImageView.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); - _photoTimeIntervalSincePostLabel.style.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer - - ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size? - spacer.style.flexGrow = 1.0; - - UIEdgeInsets avatarInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER); - ASInsetLayoutSpec *avatarInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:avatarInsets child:_userAvatarImageView]; + return + // Main stack + [ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[ - ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec]; - headerStack.alignItems = ASStackLayoutAlignItemsCenter; // center items vertically in horizontal stack - headerStack.justifyContent = ASStackLayoutJustifyContentStart; // justify content to the left side of the header stack - [headerStack setChildren:@[avatarInset, headerSubStack, spacer, _photoTimeIntervalSincePostLabel]]; - - // header inset stack - UIEdgeInsets insets = UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER); - ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:headerStack]; - - // footer stack - ASStackLayoutSpec *footerStack = [ASStackLayoutSpec verticalStackLayoutSpec]; - footerStack.spacing = VERTICAL_BUFFER; - [footerStack setChildren:@[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsView]]; - - // footer inset stack - UIEdgeInsets footerInsets = UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER); - ASInsetLayoutSpec *footerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:footerInsets child:footerStack]; - - // vertical stack - CGFloat cellWidth = constrainedSize.max.width; - - // constrain photo frame size - _photoImageView.style.preferredSize = CGSizeMake(cellWidth, cellWidth); - - ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec]; - verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space - [verticalStack setChildren:@[headerWithInset, _photoImageView, footerWithInset]]; - - return verticalStack; + // Header stack with inset + [ASInsetLayoutSpec + insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER) + child: + // Header stack + [ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:0.0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsCenter + children:@[ + // Avatar image with inset + [ASInsetLayoutSpec + insetLayoutSpecWithInsets:UIEdgeInsetsMake(HORIZONTAL_BUFFER, 0, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER) + child: + [_userAvatarImageView styledWithBlock:^(ASLayoutElementStyle *style) { + style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); + }] + ], + // User and photo location stack + [[ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:0.0 + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:_photoLocationLabel.attributedText ? @[ + [_userNameLabel styledWithBlock:^(ASLayoutElementStyle *style) { + style.flexShrink = 1.0; + }], + [_photoLocationLabel styledWithBlock:^(ASLayoutElementStyle *style) { + style.flexShrink = 1.0; + }] + ] : + @[ + [_userNameLabel styledWithBlock:^(ASLayoutElementStyle *style) { + style.flexShrink = 1.0; + }] + ]] + styledWithBlock:^(ASLayoutElementStyle *style) { + style.flexShrink = 1.0; + }], + // Spacer between user / photo location and photo time inverval + [[ASLayoutSpec new] styledWithBlock:^(ASLayoutElementStyle *style) { + style.flexGrow = 1.0; + }], + // Photo and time interval node + [_photoTimeIntervalSincePostLabel styledWithBlock:^(ASLayoutElementStyle *style) { + // to remove double spaces around spacer + style.spacingBefore = HORIZONTAL_BUFFER; + }] + ]] + ], + + // Center photo with ratio + [ASRatioLayoutSpec + ratioLayoutSpecWithRatio:1.0 + child:_photoImageView], + + // Footer stack with inset + [ASInsetLayoutSpec + insetLayoutSpecWithInsets:UIEdgeInsetsMake(VERTICAL_BUFFER, HORIZONTAL_BUFFER, VERTICAL_BUFFER, HORIZONTAL_BUFFER) + child: + [ASStackLayoutSpec + stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:VERTICAL_BUFFER + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[ + _photoLikesLabel, + _photoDescriptionLabel, + _photoCommentsView + ]] + ] + ]]; } #pragma mark - Instance Methods @@ -185,7 +216,6 @@ ASTextNode *textNode = [[ASTextNode alloc] init]; textNode.layerBacked = YES; textNode.attributedText = attributedString; - return textNode; } diff --git a/examples/ASLayoutSpecPlayground-Swift/Podfile b/examples/ASLayoutSpecPlayground-Swift/Podfile new file mode 100644 index 0000000000..dcbb49798f --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Podfile @@ -0,0 +1,7 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '7.1' +use_frameworks! +target 'Sample' do + pod 'AsyncDisplayKit', :path => '../..' +end + diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj b/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..ced4aae5c4 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/project.pbxproj @@ -0,0 +1,380 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5E6D34211DB4C9D000FB9B0A /* Sample.h in Headers */ = {isa = PBXBuildFile; fileRef = 5E6D341F1DB4C9D000FB9B0A /* Sample.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FF896945AEA7EF2D9CD93A65 /* Pods_Sample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 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 = ""; }; + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = ""; }; + 5E6D341D1DB4C9D000FB9B0A /* Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E6D341F1DB4C9D000FB9B0A /* Sample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Sample.h; sourceTree = ""; }; + 5E6D34201DB4C9D000FB9B0A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5E6D34251DB4CA8E00FB9B0A /* libPods-Sample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libPods-Sample.a"; path = "Pods/../build/Debug-iphoneos/libPods-Sample.a"; sourceTree = ""; }; + 5E6D34271DB4CBAA00FB9B0A /* Sample.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Sample.playground; sourceTree = ""; }; + 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 = ""; }; + EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5E6D34191DB4C9D000FB9B0A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF896945AEA7EF2D9CD93A65 /* Pods_Sample.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 05E2127819D4DB510098F589 = { + isa = PBXGroup; + children = ( + 5E6D341E1DB4C9D000FB9B0A /* Sample */, + 05E2128219D4DB510098F589 /* Products */, + 1A943BF0259746F18D6E423F /* Frameworks */, + 1AE410B73DA5C3BD087ACDD7 /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 05E2128219D4DB510098F589 /* Products */ = { + isa = PBXGroup; + children = ( + 5E6D341D1DB4C9D000FB9B0A /* Sample.framework */, + ); + name = Products; + sourceTree = ""; + }; + 1A943BF0259746F18D6E423F /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5E6D34251DB4CA8E00FB9B0A /* libPods-Sample.a */, + 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */, + EC767A9EB720B4BF08C89936 /* Pods_Sample.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1AE410B73DA5C3BD087ACDD7 /* Pods */ = { + isa = PBXGroup; + children = ( + C068F1D3F0CC317E895FCDAB /* Pods.debug.xcconfig */, + 088AA6578212BE9BFBB07B70 /* Pods.release.xcconfig */, + FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */, + 5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 5E6D341E1DB4C9D000FB9B0A /* Sample */ = { + isa = PBXGroup; + children = ( + 5E6D341F1DB4C9D000FB9B0A /* Sample.h */, + 5E6D34201DB4C9D000FB9B0A /* Info.plist */, + 5E6D34271DB4CBAA00FB9B0A /* Sample.playground */, + ); + path = Sample; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 5E6D341A1DB4C9D000FB9B0A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E6D34211DB4C9D000FB9B0A /* Sample.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 5E6D341C1DB4C9D000FB9B0A /* Sample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E6D34221DB4C9D000FB9B0A /* Build configuration list for PBXNativeTarget "Sample" */; + buildPhases = ( + 43927A700F47FC31FA2FB429 /* [CP] Check Pods Manifest.lock */, + 5E6D34181DB4C9D000FB9B0A /* Sources */, + 5E6D34191DB4C9D000FB9B0A /* Frameworks */, + 5E6D341A1DB4C9D000FB9B0A /* Headers */, + 5E6D341B1DB4C9D000FB9B0A /* Resources */, + 0FF30A537A157312FD5042F7 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Sample; + productName = Sample; + productReference = 5E6D341D1DB4C9D000FB9B0A /* Sample.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 05E2127919D4DB510098F589 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 5E6D341C1DB4C9D000FB9B0A = { + CreatedOnToolsVersion = 8.0; + ProvisioningStyle = Automatic; + }; + }; + }; + 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 = ( + 5E6D341C1DB4C9D000FB9B0A /* Sample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5E6D341B1DB4C9D000FB9B0A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0FF30A537A157312FD5042F7 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 43927A700F47FC31FA2FB429 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] 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 # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5E6D34181DB4C9D000FB9B0A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + 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; + ENABLE_TESTABILITY = 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 = 7.1; + 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 = 7.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E6D34231DB4C9D000FB9B0A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FDF496F367580DF9280D36EA /* Pods-Sample.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = ""; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = Sample/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5E6D34241DB4C9D000FB9B0A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5C5154389F056C672F4E9EEA /* Pods-Sample.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = ""; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = Sample/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LIBRARY_SEARCH_PATHS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.facebook.AsyncDisplayKit.Sample; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + 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; + }; + 5E6D34221DB4C9D000FB9B0A /* Build configuration list for PBXNativeTarget "Sample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E6D34231DB4C9D000FB9B0A /* Debug */, + 5E6D34241DB4C9D000FB9B0A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 05E2127919D4DB510098F589 /* Project object */; +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme b/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme new file mode 100644 index 0000000000..ccd8f696bb --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample.xcodeproj/xcshareddata/xcschemes/Sample.xcscheme @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Info.plist b/examples/ASLayoutSpecPlayground-Swift/Sample/Info.plist new file mode 100644 index 0000000000..fbe1e6b314 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.h b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.h new file mode 100644 index 0000000000..0c5d69364f --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.h @@ -0,0 +1,18 @@ +// +// Sample.h +// Sample +// +// Copyright © 2016 Facebook. All rights reserved. +// + +#import + +//! Project version number for Sample. +FOUNDATION_EXPORT double SampleVersionNumber; + +//! Project version string for Sample. +FOUNDATION_EXPORT const unsigned char SampleVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/HorizontalStackWithSpacer.xcplaygroundpage/Contents.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/HorizontalStackWithSpacer.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..10715382bb --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/HorizontalStackWithSpacer.xcplaygroundpage/Contents.swift @@ -0,0 +1,46 @@ +//: [Photo With Outset Icon Overlay](PhotoWithOutsetIconOverlay) + +import AsyncDisplayKit + +extension HorizontalStackWithSpacer { + + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + usernameNode.style.flexShrink = 1.0 + postLocationNode.style.flexShrink = 1.0 + + let verticalStackSpec = ASStackLayoutSpec.vertical() + verticalStackSpec.style.flexShrink = 1.0 + + // if fetching post location data from server, check if it is available yet + if postLocationNode.attributedText != nil { + verticalStackSpec.children = [usernameNode, postLocationNode] + } else { + verticalStackSpec.children = [usernameNode] + } + + let spacerSpec = ASLayoutSpec() + spacerSpec.style.flexGrow = 1.0 + spacerSpec.style.flexShrink = 1.0 + + // horizontal stack + let horizontalStack = ASStackLayoutSpec.horizontal() + horizontalStack.alignItems = .center // center items vertically in horiz stack + horizontalStack.justifyContent = .start // justify content to left + horizontalStack.style.flexShrink = 1.0 + horizontalStack.style.flexGrow = 1.0 + horizontalStack.children = [verticalStackSpec, spacerSpec, postTimeNode] + + // inset horizontal stack + let insets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 10) + let headerInsetSpec = ASInsetLayoutSpec(insets: insets, child: horizontalStack) + headerInsetSpec.style.flexShrink = 1.0 + headerInsetSpec.style.flexGrow = 1.0 + + return headerInsetSpec + } + +} + +HorizontalStackWithSpacer().show() + +//: [Index](Index) diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/Index.xcplaygroundpage/Contents.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/Index.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..b44cbd0226 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/Index.xcplaygroundpage/Contents.swift @@ -0,0 +1,24 @@ +/*: + ## ⚠️ You must start by building the Sample framework ⚠️ + Once that succeeds, you should not have to build until you update AsyncDisplayKit! + What you see here isn't comprehensive, but you should be able to tweak the variables to familiarize yourself with the layout APIs. + - - - + + ## Table of Contents + * [Stack Layout](StackLayout) + * [Photo With Inset Text Overlay](PhotoWithInsetTextOverlay) + * [Photo With Outset Icon Overlay](PhotoWithOutsetIconOverlay) + * [Horizontal Stack With Spacer](HorizontalStackWithSpacer) + + - - - +Tips: + 1. Make sure to show the Assistant Editor in order to preview your code changes. You can do this with either of the following: + - (cmd + opt/alt + ⮐) + - View → Assistant Editor → Show Assistant Editor, to see the preview + 1. Make sure that **Timeline** as the element selected in the Assistant Editor + 1. You might have to click on stop/start (the one at the bottom of the screen, under the editor) a few times in case the timeline isn't updating. + - - - +Solutions to Common Issues: + 1. If you're getting errors regarding **import Sample_Sources**, simply restart Xcode and try again. + 1. If you're getting issues with threading, restart the test until it works. +*/ diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithInsetTextOverlay.xcplaygroundpage/Contents.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithInsetTextOverlay.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..cc73be4b1a --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithInsetTextOverlay.xcplaygroundpage/Contents.swift @@ -0,0 +1,26 @@ +//: [Stack Layout](StackLayout) + +import AsyncDisplayKit + +let userImageHeight = 60 + +extension PhotoWithInsetTextOverlay { + + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + photoNode.style.preferredSize = CGSize(width: userImageHeight * 2, height: userImageHeight * 2) + let backgroundImageAbsoluteSpec = ASAbsoluteLayoutSpec(children: [photoNode]) + + let insets = UIEdgeInsets(top: CGFloat.infinity, left: 12, bottom: 12, right: 12) + let textInsetSpec = ASInsetLayoutSpec(insets: insets, + child: titleNode) + + let textOverlaySpec = ASOverlayLayoutSpec(child: backgroundImageAbsoluteSpec, overlay: textInsetSpec) + + return textOverlaySpec + } + +} + +PhotoWithInsetTextOverlay().show() + +//: [Photo With Outset Icon Overlay](PhotoWithOutsetIconOverlay) \ No newline at end of file diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithOutsetIconOverlay.xcplaygroundpage/Contents.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithOutsetIconOverlay.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..ab2195f393 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/PhotoWithOutsetIconOverlay.xcplaygroundpage/Contents.swift @@ -0,0 +1,28 @@ +//: [Photo With Inset Text Overlay](PhotoWithInsetTextOverlay) + +import AsyncDisplayKit + +extension PhotoWithOutsetIconOverlay { + + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + let iconWidth: CGFloat = 40 + let iconHeight: CGFloat = 40 + + iconNode.style.preferredSize = CGSize(width: iconWidth, height: iconWidth) + photoNode.style.preferredSize = CGSize(width: 150, height: 150) + + let x: CGFloat = 150 + let y: CGFloat = 0 + + iconNode.style.layoutPosition = CGPoint(x: x, y: y) + photoNode.style.layoutPosition = CGPoint(x: iconWidth * 0.5, y: iconHeight * 0.5); + + let absoluteLayoutSpec = ASAbsoluteLayoutSpec(children: [photoNode, iconNode]) + return absoluteLayoutSpec; + } + +} + +PhotoWithOutsetIconOverlay().show() + +//: [Horizontal Stack With Spacer](HorizontalStackWithSpacer) diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/StackLayout.xcplaygroundpage/Contents.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/StackLayout.xcplaygroundpage/Contents.swift new file mode 100644 index 0000000000..d9eb59db8e --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Pages/StackLayout.xcplaygroundpage/Contents.swift @@ -0,0 +1,31 @@ +//: [Index](Index) +/*: + In this example, you can experiment with stack layouts. + */ +import AsyncDisplayKit + +extension StackLayout { + + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + // Try commenting out the flexShrink to see its consequences. + subtitleNode.style.flexShrink = 1.0 + + let stackSpec = ASStackLayoutSpec(direction: .horizontal, + spacing: 5, + justifyContent: .start, + alignItems: .start, + children: [titleNode, subtitleNode]) + + let insetSpec = ASInsetLayoutSpec(insets: UIEdgeInsets(top: 5, + left: 5, + bottom: 5, + right: 5), + child: stackSpec) + return insetSpec + } + +} + +StackLayout().show() + +//: [Photo With Inset Text Overlay](PhotoWithInsetTextOverlay) \ No newline at end of file diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/ASPlayground.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/ASPlayground.swift new file mode 100644 index 0000000000..0aab532ed3 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/ASPlayground.swift @@ -0,0 +1,26 @@ +import PlaygroundSupport +import AsyncDisplayKit + +public protocol ASPlayground: class { + func display(inRect: CGRect) +} + +extension ASPlayground { + public func display(inRect rect: CGRect) { + var rect = rect + if rect.size == .zero { + rect.size = CGSize(width: 400, height: 400) + } + + guard let nodeSelf = self as? ASDisplayNode else { + assertionFailure("Class inheriting ASPlayground must be an ASDisplayNode") + return + } + + let constrainedSize = ASSizeRange(min: rect.size, max: rect.size) + _ = ASCalculateRootLayout(nodeSelf, constrainedSize) + nodeSelf.frame = rect + PlaygroundPage.current.needsIndefiniteExecution = true + PlaygroundPage.current.liveView = nodeSelf.view + } +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/HorizontalStackWithSpacer.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/HorizontalStackWithSpacer.swift new file mode 100644 index 0000000000..03338158fb --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/HorizontalStackWithSpacer.swift @@ -0,0 +1,38 @@ +import AsyncDisplayKit + +fileprivate let fontSize: CGFloat = 20 + +public class HorizontalStackWithSpacer: ASDisplayNode, ASPlayground { + public let usernameNode = ASTextNode() + public let postLocationNode = ASTextNode() + public let postTimeNode = ASTextNode() + + override public init() { + super.init() + backgroundColor = .white + + automaticallyManagesSubnodes = true + setupNodes() + } + + private func setupNodes() { + usernameNode.backgroundColor = .yellow + usernameNode.attributedText = NSAttributedString.attributedString(string: "hannahmbanana", fontSize: fontSize, color: .darkBlueColor(), firstWordColor: nil) + + postLocationNode.backgroundColor = .lightGray + postLocationNode.maximumNumberOfLines = 1; + postLocationNode.attributedText = NSAttributedString.attributedString(string: "San Fransisco, CA", fontSize: fontSize, color: .lightBlueColor(), firstWordColor: nil) + + postTimeNode.backgroundColor = .brown + postTimeNode.attributedText = NSAttributedString.attributedString(string: "30m", fontSize: fontSize, color: .lightGray, firstWordColor: nil) + } + + // This is used to expose this function for overriding in extensions + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASLayoutSpec() + } + + public func show() { + display(inRect: CGRect(x: 0, y: 0, width: 450, height: 100)) + } +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithInsetTextOverlay.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithInsetTextOverlay.swift new file mode 100644 index 0000000000..701d82f834 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithInsetTextOverlay.swift @@ -0,0 +1,33 @@ +import AsyncDisplayKit + +public class PhotoWithInsetTextOverlay: ASDisplayNode, ASPlayground { + public let photoNode = ASNetworkImageNode() + public let titleNode = ASTextNode() + + override public init() { + super.init() + backgroundColor = .white + + automaticallyManagesSubnodes = true + setupNodes() + } + + private func setupNodes() { + photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-inset-text-overlay-photo.png") + photoNode.backgroundColor = .black + + titleNode.backgroundColor = .blue + titleNode.maximumNumberOfLines = 2 + titleNode.truncationAttributedText = NSAttributedString.attributedString(string: "...", fontSize: 16, color: .white, firstWordColor: nil) + titleNode.attributedText = NSAttributedString.attributedString(string: "family fall hikes", fontSize: 16, color: .white, firstWordColor: nil) + } + + // This is used to expose this function for overriding in extensions + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASLayoutSpec() + } + + public func show() { + display(inRect: CGRect(x: 0, y: 0, width: 120, height: 120)) + } +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithOutsetIconOverlay.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithOutsetIconOverlay.swift new file mode 100644 index 0000000000..469feab7e3 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/PhotoWithOutsetIconOverlay.swift @@ -0,0 +1,33 @@ +import AsyncDisplayKit + +fileprivate let userImageHeight = 60 + +public class PhotoWithOutsetIconOverlay: ASDisplayNode, ASPlayground { + public let photoNode = ASNetworkImageNode() + public let iconNode = ASNetworkImageNode() + + override public init() { + super.init() + backgroundColor = .white + + automaticallyManagesSubnodes = true + setupNodes() + } + + private func setupNodes() { + photoNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-photo.png") + photoNode.backgroundColor = .black + + iconNode.url = URL(string: "http://asyncdisplaykit.org/static/images/layout-examples-photo-with-outset-icon-overlay-icon.png") + iconNode.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(10, .white) + } + + // This is used to expose this function for overriding in extensions + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASLayoutSpec() + } + + public func show() { + display(inRect: CGRect(x: 0, y: 0, width: 190, height: 190)) + } +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/StackLayout.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/StackLayout.swift new file mode 100644 index 0000000000..8eb4b1dbc5 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/StackLayout.swift @@ -0,0 +1,31 @@ +import AsyncDisplayKit + +public class StackLayout: ASDisplayNode, ASPlayground { + public let titleNode = ASTextNode() + public let subtitleNode = ASTextNode() + + override public init() { + super.init() + backgroundColor = .white + + automaticallyManagesSubnodes = true + setupNodes() + } + + private func setupNodes() { + titleNode.backgroundColor = .blue + titleNode.attributedText = NSAttributedString.attributedString(string: "Headline!", fontSize: 14, color: .white, firstWordColor: nil) + + subtitleNode.backgroundColor = .yellow + subtitleNode.attributedText = NSAttributedString(string: "Lorem ipsum dolor sit amet, sed ex laudem utroque meliore, at cum lucilius vituperata. Ludus mollis consulatu mei eu, esse vocent epicurei sed at. Ut cum recusabo prodesset. Ut cetero periculis sed, mundi senserit est ut. Nam ut sonet mandamus intellegebat, summo voluptaria vim ad.") + } + + // This is used to expose this function for overriding in extensions + override public func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASLayoutSpec() + } + + public func show() { + display(inRect: .zero) + } +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/Utilities.swift b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/Utilities.swift new file mode 100644 index 0000000000..db9a9c2857 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/Sources/Utilities.swift @@ -0,0 +1,43 @@ +import UIKit +import Foundation + +extension UIColor { + + static func darkBlueColor() -> UIColor { + return UIColor(red: 18.0/255.0, green: 86.0/255.0, blue: 136.0/255.0, alpha: 1.0) + } + + + static func lightBlueColor() -> UIColor { + return UIColor(red: 0.0, green: 122.0/255.0, blue: 1.0, alpha: 1.0) + } + + static func duskColor() -> UIColor { + return UIColor(red: 255/255.0, green: 181/255.0, blue: 68/255.0, alpha: 1.0) + } + + static func customOrangeColor() -> UIColor { + return UIColor(red: 40/255.0, green: 43/255.0, blue: 53/255.0, alpha: 1.0) + } + +} + +extension NSAttributedString { + + static func attributedString(string: String, fontSize size: CGFloat, color: UIColor?, firstWordColor: UIColor?) -> NSAttributedString { + let attributes = [NSForegroundColorAttributeName: color ?? UIColor.black, + NSFontAttributeName: UIFont.boldSystemFont(ofSize: size)] + + let attributedString = NSMutableAttributedString(string: string, attributes: attributes) + + if let firstWordColor = firstWordColor { + let nsString = string as NSString + let firstSpaceRange = nsString.rangeOfCharacter(from: NSCharacterSet.whitespaces) + let firstWordRange = NSMakeRange(0, firstSpaceRange.location) + attributedString.addAttribute(NSForegroundColorAttributeName, value: firstWordColor, range: firstWordRange) + } + + return attributedString + } + +} diff --git a/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/contents.xcplayground b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/contents.xcplayground new file mode 100644 index 0000000000..c7f819f0e3 --- /dev/null +++ b/examples/ASLayoutSpecPlayground-Swift/Sample/Sample.playground/contents.xcplayground @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/ASMapNode/Sample/AppDelegate.m b/examples/ASMapNode/Sample/AppDelegate.m index e56bcd4ec0..7cf75f8bb7 100644 --- a/examples/ASMapNode/Sample/AppDelegate.m +++ b/examples/ASMapNode/Sample/AppDelegate.m @@ -24,12 +24,14 @@ @implementation AppDelegate -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ // Override point for customization after application launch. self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[ViewController new]]; [self.window makeKeyAndVisible]; + return YES; } diff --git a/examples/ASMapNode/Sample/MapHandlerNode.m b/examples/ASMapNode/Sample/MapHandlerNode.m index 1a1cd8e314..33e09a5b69 100644 --- a/examples/ASMapNode/Sample/MapHandlerNode.m +++ b/examples/ASMapNode/Sample/MapHandlerNode.m @@ -18,24 +18,18 @@ #import "MapHandlerNode.h" #import "CustomMapAnnotation.h" -#import - -#import -#import -#import - -#import -#import +#import +#import @interface MapHandlerNode () -@property (nonatomic, strong) ASEditableTextNode * latEditableNode; -@property (nonatomic, strong) ASEditableTextNode * lonEditableNode; -@property (nonatomic, strong) ASEditableTextNode * deltaLatEditableNode; -@property (nonatomic, strong) ASEditableTextNode * deltaLonEditableNode; -@property (nonatomic, strong) ASButtonNode * updateRegionButton; -@property (nonatomic, strong) ASButtonNode * liveMapToggleButton; -@property (nonatomic, strong) ASMapNode * mapNode; +@property (nonatomic, strong) ASEditableTextNode *latEditableNode; +@property (nonatomic, strong) ASEditableTextNode *lonEditableNode; +@property (nonatomic, strong) ASEditableTextNode *deltaLatEditableNode; +@property (nonatomic, strong) ASEditableTextNode *deltaLonEditableNode; +@property (nonatomic, strong) ASButtonNode *updateRegionButton; +@property (nonatomic, strong) ASButtonNode *liveMapToggleButton; +@property (nonatomic, strong) ASMapNode *mapNode; @end @@ -47,25 +41,45 @@ { if (!(self = [super init])) return nil; + + self.automaticallyManagesSubnodes = YES; + + _mapNode = [[ASMapNode alloc] init]; + _mapNode.mapDelegate = self; _latEditableNode = [[ASEditableTextNode alloc] init]; _lonEditableNode = [[ASEditableTextNode alloc] init]; _deltaLatEditableNode = [[ASEditableTextNode alloc] init]; _deltaLonEditableNode = [[ASEditableTextNode alloc] init]; - _updateRegionButton = [[ASButtonNode alloc] init]; - _liveMapToggleButton = [[ASButtonNode alloc] init]; - _mapNode = [[ASMapNode alloc] init]; - - [self addSubnode:_latEditableNode]; - [self addSubnode:_lonEditableNode]; - [self addSubnode:_deltaLatEditableNode]; - [self addSubnode:_deltaLonEditableNode]; - - [self addSubnode:_updateRegionButton]; - [self addSubnode:_liveMapToggleButton]; - [self addSubnode:_mapNode]; - + _updateRegionButton = [[ASButtonNode alloc] init]; + _liveMapToggleButton = [[ASButtonNode alloc] init]; + + UIImage *backgroundImage = [UIImage as_resizableRoundedImageWithCornerRadius:5 + cornerColor:[UIColor whiteColor] + fillColor:[UIColor lightGrayColor]]; + + UIImage *backgroundHiglightedImage = [UIImage as_resizableRoundedImageWithCornerRadius:5 + cornerColor:[UIColor whiteColor] + fillColor:[[UIColor lightGrayColor] colorWithAlphaComponent:0.4] + borderColor:[UIColor lightGrayColor] + borderWidth:2.0]; + + [_updateRegionButton setBackgroundImage:backgroundImage forState:ASControlStateNormal]; + [_updateRegionButton setBackgroundImage:backgroundHiglightedImage forState:ASControlStateHighlighted]; + + [_liveMapToggleButton setBackgroundImage:backgroundImage forState:ASControlStateNormal]; + [_liveMapToggleButton setBackgroundImage:backgroundHiglightedImage forState:ASControlStateHighlighted]; + + _updateRegionButton.contentEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5); + [_updateRegionButton setTitle:@"Update Region" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal]; + + [_updateRegionButton addTarget:self action:@selector(updateRegion) forControlEvents:ASControlNodeEventTouchUpInside]; + + [_liveMapToggleButton setTitle:[self liveMapStr] withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal]; + + [_liveMapToggleButton addTarget:self action:@selector(toggleLiveMap) forControlEvents:ASControlNodeEventTouchUpInside]; + return self; } @@ -73,24 +87,12 @@ { [super didLoad]; - _latEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _mapNode.region.center.latitude]]; - _lonEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _mapNode.region.center.longitude]]; - _deltaLatEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _mapNode.region.span.latitudeDelta]]; - _deltaLonEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", _mapNode.region.span.longitudeDelta]]; - [self configureEditableNodes:_latEditableNode]; [self configureEditableNodes:_lonEditableNode]; [self configureEditableNodes:_deltaLatEditableNode]; [self configureEditableNodes:_deltaLonEditableNode]; - - _mapNode.mapDelegate = self; - - [_updateRegionButton setTitle:@"Update Region" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal]; - [_updateRegionButton setTitle:@"Update Region" withFont:[UIFont systemFontOfSize:14] withColor:[UIColor blueColor] forState:ASControlStateHighlighted]; - [_updateRegionButton addTarget:self action:@selector(updateRegion) forControlEvents:ASControlNodeEventTouchUpInside]; - [_liveMapToggleButton setTitle:[self liveMapStr] withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal]; - [_liveMapToggleButton setTitle:[self liveMapStr] withFont:[UIFont systemFontOfSize:14] withColor:[UIColor blueColor] forState:ASControlStateHighlighted]; - [_liveMapToggleButton addTarget:self action:@selector(toggleLiveMap) forControlEvents:ASControlNodeEventTouchUpInside]; + + [self updateLocationTextWithMKCoordinateRegion:_mapNode.region]; // avoiding retain cycles __weak MapHandlerNode *weakSelf = self; @@ -109,90 +111,87 @@ [self addAnnotations]; } -#pragma mark - Layout - -- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize -{ +/** + * ---------------------------------ASStackLayoutSpec-------------------------------- + * | ------------------------------ASInsetLayoutSpec----------------------------- | + * | | ---------------------------ASStackLayoutSpec-------------------------- | | + * | | | -----------------ASStackLayoutSpec---------------- | | | + * | | | | --------------ASStackLayoutSpec------------- | | | | + * | | | | | ASEditableTextNode ASEditableTextNode | | | | | + * | | | | -------------------------------------------- | | | | + * | | | | --------------ASStackLayoutSpec------------- | ASButtonNode | | | + * | | | | | ASEditableTextNode ASEditableTextNode | | | | | + * | | | | -------------------------------------------- | | | | + * | | | -------------------------------------------------- | | | + * | | ---------------------------------------------------------------------- | | + * | ---------------------------------------------------------------------------- | + * | ASButtonNode | + * | ASMapNode | + * ---------------------------------------------------------------------------------- + * + * This diagram was created by setting a breakpoint on the returned `layoutSpec` + * and calling "po [layoutSpec asciiArtString]" in the debugger. + */ #define SPACING 5 #define HEIGHT 30 - CGSize nodeSize = CGSizeMake(constrainedSize.max.width * 0.3, HEIGHT); +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + _latEditableNode.style.width = ASDimensionMake(@"50%"); + _lonEditableNode.style.width = ASDimensionMake(@"50%"); + _deltaLatEditableNode.style.width = ASDimensionMake(@"50%"); + _deltaLonEditableNode.style.width = ASDimensionMake(@"50%"); - _latEditableNode.style.preferredSize = nodeSize; - _lonEditableNode.style.preferredSize = nodeSize; + _liveMapToggleButton.style.maxHeight = ASDimensionMake(HEIGHT); - _deltaLatEditableNode.style.preferredSize = nodeSize; - _deltaLonEditableNode.style.preferredSize = nodeSize; + _mapNode.style.flexGrow = 1.0; + + ASStackLayoutSpec *lonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsCenter + children:@[_latEditableNode, _lonEditableNode]]; + + ASStackLayoutSpec *deltaLonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentSpaceBetween + alignItems:ASStackLayoutAlignItemsCenter + children:@[_deltaLatEditableNode, _deltaLonEditableNode]]; + + ASStackLayoutSpec *lonlatConfigSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[lonlatSpec, deltaLonlatSpec]]; - _updateRegionButton.style.preferredSize = nodeSize; - _liveMapToggleButton.style.preferredSize = nodeSize; + lonlatConfigSpec.style.flexGrow = 1.0; - _latEditableNode.style.flexGrow = _lonEditableNode.style.flexGrow = YES; - _deltaLatEditableNode.style.flexGrow = _deltaLonEditableNode.style.flexGrow = YES; - _updateRegionButton.style.flexGrow = _liveMapToggleButton.style.flexGrow = YES; + ASStackLayoutSpec *dashboardSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[lonlatConfigSpec, _updateRegionButton]]; + + ASStackLayoutSpec *headerVerticalStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[dashboardSpec, _liveMapToggleButton]]; + + dashboardSpec.style.flexGrow = 1.0; - _mapNode.style.flexGrow = YES; - - ASStackLayoutSpec *lonlatSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsCenter - children:@[_latEditableNode, _lonEditableNode]]; - lonlatSpec.style.flexGrow = true; - - ASStackLayoutSpec *deltaLonlatSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsCenter - children:@[_deltaLatEditableNode, _deltaLonEditableNode]]; - deltaLonlatSpec.style.flexGrow = true; - - ASStackLayoutSpec *lonlatConfigSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsStretch - children:@[lonlatSpec, deltaLonlatSpec]]; - lonlatConfigSpec.style.flexGrow = true; - - ASStackLayoutSpec *buttonsSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsStretch - children:@[_updateRegionButton, _liveMapToggleButton]]; - buttonsSpec.style.flexGrow = true; - - ASStackLayoutSpec *dashboardSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsStretch - children:@[lonlatConfigSpec, buttonsSpec]]; - dashboardSpec.style.flexGrow = true; - - ASInsetLayoutSpec *insetSpec = - [ASInsetLayoutSpec - insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 10, 0, 10) - child:dashboardSpec]; - - ASStackLayoutSpec *layoutSpec = - [ASStackLayoutSpec - stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical - spacing:SPACING - justifyContent:ASStackLayoutJustifyContentStart - alignItems:ASStackLayoutAlignItemsStretch - children:@[insetSpec, _mapNode ]]; + ASInsetLayoutSpec *insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(40, 10, 0, 10) + child:headerVerticalStack]; + + ASStackLayoutSpec *layoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical + spacing:SPACING + justifyContent:ASStackLayoutJustifyContentStart + alignItems:ASStackLayoutAlignItemsStretch + children:@[insetSpec, _mapNode]]; + return layoutSpec; } -#pragma mark - Button actions +#pragma mark - Button Actions - (void)updateRegion { @@ -204,7 +203,8 @@ double const deltaLat = [f numberFromString:_deltaLatEditableNode.attributedText.string].doubleValue; double const deltaLon = [f numberFromString:_deltaLonEditableNode.attributedText.string].doubleValue; - MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(lat, lon), + // TODO: check for valid latitude / longitude coordinates + MKCoordinateRegion region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(lat, lon), MKCoordinateSpanMake(deltaLat, deltaLon)); _mapNode.region = region; @@ -218,17 +218,30 @@ [_liveMapToggleButton setTitle:liveMapStr withFont:[UIFont systemFontOfSize:14] withColor:[UIColor blueColor] forState:ASControlStateHighlighted]; } -#pragma mark - Helpers +- (void)updateLocationTextWithMKCoordinateRegion:(MKCoordinateRegion)region +{ + _latEditableNode.attributedText = [self attributedStringFromFloat:region.center.latitude]; + _lonEditableNode.attributedText = [self attributedStringFromFloat:region.center.longitude]; + _deltaLatEditableNode.attributedText = [self attributedStringFromFloat:region.span.latitudeDelta]; + _deltaLonEditableNode.attributedText = [self attributedStringFromFloat:region.span.longitudeDelta]; +} + +#pragma mark - Helper Methods + +- (NSAttributedString *)attributedStringFromFloat:(CGFloat)value +{ + return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%0.3f", value]]; +} - (void)addAnnotations { MKPointAnnotation *brno = [MKPointAnnotation new]; brno.coordinate = CLLocationCoordinate2DMake(49.2002211, 16.6078411); - brno.title = @"Brno city"; + brno.title = @"Brno City"; CustomMapAnnotation *atlantic = [CustomMapAnnotation new]; atlantic.coordinate = CLLocationCoordinate2DMake(38.6442228, -29.9956942); - atlantic.title = @"Atlantic ocean"; + atlantic.title = @"Atlantic Ocean"; atlantic.image = [UIImage imageNamed:@"Water"]; CustomMapAnnotation *kilimanjaro = [CustomMapAnnotation new]; @@ -299,6 +312,7 @@ - (MKAnnotationView *)annotationViewForAnnotation:(id)annotation { MKAnnotationView *av; + if ([annotation isKindOfClass:[CustomMapAnnotation class]]) { av = [[MKAnnotationView alloc] init]; av.centerOffset = CGPointMake(21, 21); @@ -306,18 +320,17 @@ } else { av = [[MKPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:@""]; } - + av.opaque = NO; + return av; } #pragma mark - MKMapViewDelegate -- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { - _latEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", mapView.region.center.latitude]]; - _lonEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", mapView.region.center.longitude]]; - _deltaLatEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", mapView.region.span.latitudeDelta]]; - _deltaLonEditableNode.attributedText = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%f", mapView.region.span.longitudeDelta]]; +- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated +{ + [self updateLocationTextWithMKCoordinateRegion:mapView.region]; } - (MKAnnotationView *)mapView:(MKMapView *)__unused mapView viewForAnnotation:(id)annotation diff --git a/examples/ASMapNode/Sample/ViewController.m b/examples/ASMapNode/Sample/ViewController.m index 53967e2035..4dc690ae34 100644 --- a/examples/ASMapNode/Sample/ViewController.m +++ b/examples/ASMapNode/Sample/ViewController.m @@ -36,18 +36,11 @@ return self; } -#pragma mark - UIViewController - -- (void)viewDidLoad -{ - [super viewDidLoad]; - -} - - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - self.navigationController.navigationBarHidden = true; + + self.navigationController.navigationBarHidden = YES; } @end diff --git a/examples/ASViewController/Sample/ViewController.m b/examples/ASViewController/Sample/ViewController.m index b7014b6c8c..2b3cb1d229 100644 --- a/examples/ASViewController/Sample/ViewController.m +++ b/examples/ASViewController/Sample/ViewController.m @@ -66,7 +66,7 @@ { [super viewWillAppear:animated]; - [self.tableNode.view deselectRowAtIndexPath:self.tableNode.view.indexPathForSelectedRow animated:YES]; + [self.tableNode deselectRowAtIndexPath:self.tableNode.view.indexPathForSelectedRow animated:YES]; } diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m index d58b6862a5..78c8615ede 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m @@ -53,12 +53,12 @@ #pragma mark - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section { return 100; } -- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath { return ^{ ASTextCellNode *cellNode = [ASTextCellNode new]; @@ -68,7 +68,7 @@ }; } -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath { return ASSizeRangeMake(CGSizeMake(100, 100)); } diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m index 3d1e65ddba..1a94fdbf84 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m @@ -51,12 +51,12 @@ #pragma mark - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { return 100; } -- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { return ^{ ASTextCellNode *cellNode = [ASTextCellNode new]; diff --git a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.h b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.h index 880cf78a58..9e838b4fba 100644 --- a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.h +++ b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.h @@ -17,7 +17,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import +#import @protocol ASLayoutSpecListEntry @@ -27,7 +27,7 @@ @end -@interface OverviewComponentsViewController : UIViewController +@interface OverviewComponentsViewController : ASViewController @end diff --git a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m index 585c4c35f2..c343d74a65 100644 --- a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m +++ b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m @@ -31,12 +31,15 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constrainedSize); + #pragma mark - OverviewDisplayNodeWithSizeBlock @interface OverviewDisplayNodeWithSizeBlock : ASDisplayNode + @property (nonatomic, copy) NSString *entryTitle; @property (nonatomic, copy) NSString *entryDescription; @property (nonatomic, copy) OverviewDisplayNodeSizeThatFitsBlock sizeThatFitsBlock; + @end @implementation OverviewDisplayNodeWithSizeBlock @@ -54,11 +57,14 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr @end + #pragma mark - OverviewTitleDescriptionCellNode @interface OverviewTitleDescriptionCellNode : ASCellNode + @property (nonatomic, strong) ASTextNode *titleNode; @property (nonatomic, strong) ASTextNode *descriptionNode; + @end @implementation OverviewTitleDescriptionCellNode @@ -91,16 +97,35 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr @end + #pragma mark - OverviewComponentsViewController @interface OverviewComponentsViewController () + @property (nonatomic, copy) NSArray *data; @property (nonatomic, strong) ASTableNode *tableNode; + @end @implementation OverviewComponentsViewController -#pragma mark - UIViewController + +#pragma mark - Lifecycle Methods + +- (instancetype)init +{ + _tableNode = [ASTableNode new]; + + self = [super initWithNode:_tableNode]; + + if (self) { + _tableNode.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _tableNode.delegate = (id)self; + _tableNode.dataSource = (id)self; + } + + return self; +} - (void)viewDidLoad { @@ -109,18 +134,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr self.title = @"AsyncDisplayKit"; [self setupData]; - [self setupTableNode]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - ASTableView *tableView = self.tableNode.view; - [tableView deselectRowAtIndexPath:tableView.indexPathForSelectedRow animated:YES]; + [_tableNode deselectRowAtIndexPath:_tableNode.view.indexPathForSelectedRow animated:YES]; } -#pragma mark - Setup + +#pragma mark - Data Model - (void)setupData { @@ -452,16 +476,6 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr self.data = mutableData; } -- (void)setupTableNode -{ - _tableNode = [ASTableNode new]; - _tableNode.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _tableNode.frame = self.view.bounds; - _tableNode.delegate = (id)self; - _tableNode.dataSource = (id)self; - [self.view addSubnode:_tableNode]; -} - #pragma mark - Parent / Child Helper - (OverviewDisplayNodeWithSizeBlock *)parentNodeWithChild:(ASDisplayNode *)child @@ -505,22 +519,22 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr #pragma mark - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView +- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode { return self.data.count; } -- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section +- (nullable NSString *)tableNode:(ASTableNode *)tableNode titleForHeaderInSection:(NSInteger)section { return self.data[section][@"title"]; } -- (NSInteger)tableView:(ASTableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { return [self.data[section][@"data"] count]; } -- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { // You should get the node or data you want to pass to the cell node outside of the ASCellNodeBlock ASDisplayNode *node = self.data[indexPath.section][@"data"][indexPath.row]; @@ -542,7 +556,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr }; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ASDisplayNode *node = self.data[indexPath.section][@"data"][indexPath.row]; OverviewDetailViewController *detail = [[OverviewDetailViewController alloc] initWithNode:node]; diff --git a/examples/CatDealsCollectionView/Sample/ViewController.m b/examples/CatDealsCollectionView/Sample/ViewController.m index 4cec349673..8726968ef4 100644 --- a/examples/CatDealsCollectionView/Sample/ViewController.m +++ b/examples/CatDealsCollectionView/Sample/ViewController.m @@ -29,7 +29,7 @@ static const NSInteger kBatchSize = 20; static const CGFloat kHorizontalSectionPadding = 10.0f; static const CGFloat kVerticalSectionPadding = 20.0f; -@interface ViewController () +@interface ViewController () { ASCollectionNode *_collectionNode; NSMutableArray *_data; @@ -45,15 +45,15 @@ static const CGFloat kVerticalSectionPadding = 20.0f; - (instancetype)init { + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + self = [super initWithNode:_collectionNode]; if (self) { self.title = @"Cat Deals"; - - UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - - _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + _collectionNode.dataSource = self; _collectionNode.delegate = self; _collectionNode.backgroundColor = [UIColor grayColor]; @@ -63,10 +63,10 @@ static const CGFloat kVerticalSectionPadding = 20.0f; preloadTuning.trailingBufferScreenfuls = 1; [_collectionNode setTuningParameters:preloadTuning forRangeType:ASLayoutRangeTypePreload]; - ASRangeTuningParameters preRenderTuning; - preRenderTuning.leadingBufferScreenfuls = 1; - preRenderTuning.trailingBufferScreenfuls = 0.5; - [_collectionNode setTuningParameters:preRenderTuning forRangeType:ASLayoutRangeTypeDisplay]; + ASRangeTuningParameters displayTuning; + displayTuning.leadingBufferScreenfuls = 1; + displayTuning.trailingBufferScreenfuls = 0.5; + [_collectionNode setTuningParameters:displayTuning forRangeType:ASLayoutRangeTypeDisplay]; [_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; [_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; @@ -151,10 +151,9 @@ static const CGFloat kVerticalSectionPadding = 20.0f; [_collectionNode reloadData]; } -#pragma mark - -#pragma mark ASCollectionView data source. +#pragma mark - ASCollectionNodeDelegate / ASCollectionNodeDataSource -- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath { ItemViewModel *viewModel = _data[indexPath.item]; return ^{ @@ -162,7 +161,8 @@ static const CGFloat kVerticalSectionPadding = 20.0f; }; } -- (ASCellNode *)collectionView:(UICollectionView *)collectionView nodeForSupplementaryElementOfKind:(nonnull NSString *)kind atIndexPath:(nonnull NSIndexPath *)indexPath { +- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ if ([kind isEqualToString:UICollectionElementKindSectionHeader] && indexPath.section == 0) { return [[BlurbNode alloc] init]; } else if ([kind isEqualToString:UICollectionElementKindSectionFooter] && indexPath.section == 0) { @@ -171,6 +171,44 @@ static const CGFloat kVerticalSectionPadding = 20.0f; return nil; } +- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + CGFloat collectionViewWidth = CGRectGetWidth(self.view.frame) - 2 * kHorizontalSectionPadding; + CGFloat oneItemWidth = [ItemNode preferredViewSize].width; + NSInteger numColumns = floor(collectionViewWidth / oneItemWidth); + // Number of columns should be at least 1 + numColumns = MAX(1, numColumns); + + CGFloat totalSpaceBetweenColumns = (numColumns - 1) * kHorizontalSectionPadding; + CGFloat itemWidth = ((collectionViewWidth - totalSpaceBetweenColumns) / numColumns); + CGSize itemSize = [ItemNode sizeForWidth:itemWidth]; + return ASSizeRangeMake(itemSize, itemSize); +} + +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +{ + return [_data count]; +} + +- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode +{ + return 1; +} + +- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context +{ + NSLog(@"fetch additional content"); + [self fetchMoreCatsWithCompletion:^(BOOL finished){ + [context completeBatchFetching:YES]; + }]; +} + +#pragma mark - ASCollectionViewDelegateFlowLayout + +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { + return UIEdgeInsetsMake(kVerticalSectionPadding, kHorizontalSectionPadding, kVerticalSectionPadding, kHorizontalSectionPadding); +} + - (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { if (section == 0) { CGFloat width = CGRectGetWidth(self.view.frame) - 2 * kHorizontalSectionPadding; @@ -187,55 +225,5 @@ static const CGFloat kVerticalSectionPadding = 20.0f; return CGSizeZero; } -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - CGFloat collectionViewWidth = CGRectGetWidth(self.view.frame) - 2 * kHorizontalSectionPadding; - CGFloat oneItemWidth = [ItemNode preferredViewSize].width; - NSInteger numColumns = floor(collectionViewWidth / oneItemWidth); - // Number of columns should be at least 1 - numColumns = MAX(1, numColumns); - - CGFloat totalSpaceBetweenColumns = (numColumns - 1) * kHorizontalSectionPadding; - CGFloat itemWidth = ((collectionViewWidth - totalSpaceBetweenColumns) / numColumns); - CGSize itemSize = [ItemNode sizeForWidth:itemWidth]; - return ASSizeRangeMake(itemSize, itemSize); -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - return [_data count]; -} - -- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView -{ - return 1; -} - -- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView -{ - // lock the data source - // The data source should not be change until it is unlocked. -} - -- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView -{ - // unlock the data source to enable data source updating. -} - -- (void)collectionView:(UICollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context -{ - NSLog(@"fetch additional content"); - [self fetchMoreCatsWithCompletion:^(BOOL finished){ - [context completeBatchFetching:YES]; - }]; -} - -- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { - return UIEdgeInsetsMake(kVerticalSectionPadding, kHorizontalSectionPadding, kVerticalSectionPadding, kHorizontalSectionPadding); -} - --(void)dealloc -{ - NSLog(@"ViewController is deallocing"); -} @end diff --git a/examples/CustomCollectionView/Sample/AppDelegate.m b/examples/CustomCollectionView/Sample/AppDelegate.m index 853bf6e8bc..c0769e5d5a 100644 --- a/examples/CustomCollectionView/Sample/AppDelegate.m +++ b/examples/CustomCollectionView/Sample/AppDelegate.m @@ -26,7 +26,7 @@ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window.backgroundColor = [UIColor whiteColor]; self.window.rootViewController = [[ViewController alloc] init]; - + [self.window makeKeyAndVisible]; return YES; diff --git a/examples/CustomCollectionView/Sample/ViewController.m b/examples/CustomCollectionView/Sample/ViewController.m index a7fe524811..5d070ebcd6 100644 --- a/examples/CustomCollectionView/Sample/ViewController.m +++ b/examples/CustomCollectionView/Sample/ViewController.m @@ -39,6 +39,17 @@ static NSUInteger kNumberOfImages = 14; - (instancetype)init { + MosaicCollectionViewLayout *layout = [[MosaicCollectionViewLayout alloc] init]; + layout.numberOfColumns = 2; + layout.headerHeight = 44.0; + + _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + _collectionNode.dataSource = self; + _collectionNode.delegate = self; + _collectionNode.backgroundColor = [UIColor whiteColor]; + + _layoutInspector = [[MosaicCollectionViewLayoutInspector alloc] init]; + if (!(self = [super initWithNode:_collectionNode])) return nil; @@ -53,16 +64,6 @@ static NSUInteger kNumberOfImages = 14; } } - MosaicCollectionViewLayout *layout = [[MosaicCollectionViewLayout alloc] init]; - layout.numberOfColumns = 2; - layout.headerHeight = 44.0; - - _layoutInspector = [[MosaicCollectionViewLayoutInspector alloc] init]; - - _collectionNode.dataSource = self; - _collectionNode.delegate = self; - _collectionNode.backgroundColor = [UIColor whiteColor]; - [_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; return self; @@ -75,19 +76,12 @@ static NSUInteger kNumberOfImages = 14; _collectionNode.view.layoutInspector = _layoutInspector; } -- (void)dealloc -{ - _collectionNode.dataSource = nil; - _collectionNode.delegate = nil; -} - - (void)reloadTapped { [_collectionNode reloadData]; } -#pragma mark - -#pragma mark ASCollectionView data source. +#pragma mark - ASCollectionNode data source. - (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -110,12 +104,12 @@ static NSUInteger kNumberOfImages = 14; return textCellNode; } -- (NSInteger)numberOfSectionsInCollectionView:(ASCollectionNode *)collectionNode +- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode { return _sections.count; } -- (NSInteger)collectionView:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section { return [_sections[section] count]; } diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm index e070c4fbe9..9f2f355e9a 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm +++ b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm @@ -39,6 +39,8 @@ static const CGFloat kInnerPadding = 10.0f; @implementation HorizontalScrollCellNode +#pragma mark - Lifecycle + - (instancetype)initWithElementSize:(CGSize)size { if (!(self = [super init])) @@ -46,11 +48,16 @@ static const CGFloat kInnerPadding = 10.0f; _elementSize = size; + // the containing table uses -nodeForRowAtIndexPath (rather than -nodeBlockForRowAtIndexPath), + // so this init method will always be run on the main thread (thus it is safe to do UIKit things). UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; flowLayout.itemSize = _elementSize; flowLayout.minimumInteritemSpacing = kInnerPadding; + _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:flowLayout]; + _collectionNode.delegate = self; + _collectionNode.dataSource = self; [self addSubnode:_collectionNode]; // hairline cell separator @@ -61,40 +68,6 @@ static const CGFloat kInnerPadding = 10.0f; return self; } -- (void)didLoad -{ - [super didLoad]; - - _collectionNode.view.asyncDelegate = self; - _collectionNode.view.asyncDataSource = self; -} - -- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section -{ - return 5; -} - -- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath -{ - CGSize elementSize = _elementSize; - return ^{ - RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; - elementNode.style.preferredSize = elementSize; - return elementNode; - }; -} - -- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize -{ - CGSize collectionNodeSize = CGSizeMake(constrainedSize.max.width, _elementSize.height); - _collectionNode.style.preferredSize = collectionNodeSize; - - 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 { @@ -107,4 +80,34 @@ static const CGFloat kInnerPadding = 10.0f; _divider.frame = CGRectMake(0.0f, 0.0f, self.calculatedSize.width, pixelHeight); } +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + CGSize collectionNodeSize = CGSizeMake(constrainedSize.max.width, _elementSize.height); + _collectionNode.style.preferredSize = collectionNodeSize; + + ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init]; + insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, 0.0, kOuterPadding, 0.0); + insetSpec.child = _collectionNode; + + return insetSpec; +} + +#pragma mark - ASCollectionNode + +- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section +{ + return 5; +} + +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath +{ + CGSize elementSize = _elementSize; + + return ^{ + RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; + elementNode.style.preferredSize = elementSize; + return elementNode; + }; +} + @end diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m index 56ba5e22ab..876775f35c 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m +++ b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m @@ -34,13 +34,13 @@ - (instancetype)init { - if (!(self = [super initWithNode:_tableNode])) - return nil; - _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; _tableNode.dataSource = self; _tableNode.delegate = self; - + + if (!(self = [super initWithNode:_tableNode])) + return nil; + self.title = @"Horizontal Scrolling Gradients"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRedo target:self @@ -61,14 +61,14 @@ [_tableNode reloadData]; } -#pragma mark - ASTableView. +#pragma mark - ASTableNode -- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath { return [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)]; } -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { return 100; } diff --git a/examples/Kittens/Sample/ViewController.h b/examples/Kittens/Sample/ViewController.h index fc52c022f2..db689fe324 100644 --- a/examples/Kittens/Sample/ViewController.h +++ b/examples/Kittens/Sample/ViewController.h @@ -15,8 +15,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import +#import -@interface ViewController : UIViewController +@interface ViewController : ASViewController @end diff --git a/examples/Kittens/Sample/ViewController.m b/examples/Kittens/Sample/ViewController.m index b4f0ad01b0..6b2e4b5058 100644 --- a/examples/Kittens/Sample/ViewController.m +++ b/examples/Kittens/Sample/ViewController.m @@ -24,13 +24,13 @@ #import "KittenNode.h" -static const NSInteger kLitterSize = 20; // intial number of kitten cells in ASTableView -static const NSInteger kLitterBatchSize = 10; // number of kitten cells to add to ASTableView -static const NSInteger kMaxLitterSize = 100; // max number of kitten cells allowed in ASTableView +static const NSInteger kLitterSize = 20; // intial number of kitten cells in ASTableNode +static const NSInteger kLitterBatchSize = 10; // number of kitten cells to add to ASTableNode +static const NSInteger kMaxLitterSize = 100; // max number of kitten cells allowed in ASTableNode -@interface ViewController () +@interface ViewController () { - ASTableView *_tableView; + ASTableNode *_tableNode; // array of boxed CGSizes corresponding to placekitten.com kittens NSMutableArray *_kittenDataSource; @@ -47,32 +47,40 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell @implementation ViewController -#pragma mark - -#pragma mark UIViewController. +#pragma mark - Lifecycle - (instancetype)init { - if (!(self = [super init])) - return nil; + _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + _tableNode.dataSource = self; + _tableNode.delegate = self; - _tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; - _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // KittenNode has its own separator - _tableView.asyncDataSource = self; - _tableView.asyncDelegate = self; + if (!(self = [super initWithNode:_tableNode])) + return nil; + + _tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone; // KittenNode has its own separator // populate our "data source" with some random kittens _kittenDataSource = [self createLitterWithSize:kLitterSize]; - _blurbNodeIndexPath = [NSIndexPath indexPathForItem:0 inSection:0]; self.title = @"Kittens"; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:@selector(toggleEditingMode)]; - + return self; } +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.node addSubnode:_tableNode]; +} + +#pragma mark - Data Model + - (NSMutableArray *)createLitterWithSize:(NSInteger)litterSize { NSMutableArray *kittens = [NSMutableArray arrayWithCapacity:litterSize]; @@ -95,36 +103,21 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell _kittenDataSource = kittenDataSource; } -- (void)viewDidLoad -{ - [super viewDidLoad]; - - [self.view addSubview:_tableView]; -} - -- (void)viewWillLayoutSubviews -{ - _tableView.frame = self.view.bounds; -} - - (void)toggleEditingMode { - [_tableView setEditing:!_tableView.editing animated:YES]; + [_tableNode.view setEditing:!_tableNode.view.editing animated:YES]; } -#pragma mark - -#pragma mark ASTableView. +#pragma mark - ASTableNode -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { - [_tableView deselectRowAtIndexPath:indexPath animated:YES]; - // Assume only kitten nodes are selectable (see -tableView:shouldHighlightRowAtIndexPath:). - KittenNode *node = (KittenNode *)[_tableView nodeForRowAtIndexPath:indexPath]; - [node toggleImageEnlargement]; + // blurb node + kLitterSize kitties + return 1 + _kittenDataSource.count; } -- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath { // special-case the first row if ([_blurbNodeIndexPath compare:indexPath] == NSOrderedSame) { @@ -137,34 +130,23 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell return node; } -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - // blurb node + kLitterSize kitties - return 1 + _kittenDataSource.count; + [_tableNode deselectRowAtIndexPath:indexPath animated:YES]; + + // Assume only kitten nodes are selectable (see -tableNode:shouldHighlightRowAtIndexPath:). + KittenNode *node = (KittenNode *)[_tableNode nodeForRowAtIndexPath:indexPath]; + + [node toggleImageEnlargement]; } -- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath +- (BOOL)tableNode:(ASTableNode *)tableNode shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { // Enable selection for kitten nodes return [_blurbNodeIndexPath compare:indexPath] != NSOrderedSame; } -- (void)tableViewLockDataSource:(ASTableView *)tableView -{ - self.dataSourceLocked = YES; -} - -- (void)tableViewUnlockDataSource:(ASTableView *)tableView -{ - self.dataSourceLocked = NO; -} - -- (BOOL)shouldBatchFetchForTableView:(UITableView *)tableView -{ - return _kittenDataSource.count < kMaxLitterSize; -} - -- (void)tableView:(UITableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context +- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(nonnull ASBatchContext *)context { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // populate a new array of random-sized kittens @@ -181,24 +163,30 @@ static const NSInteger kMaxLitterSize = 100; // max number of kitten cell // add new kittens to the data source & notify table of new indexpaths [_kittenDataSource addObjectsFromArray:moarKittens]; - [tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; + [tableNode insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; [context completeBatchFetching:YES]; }); } +- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode +{ + return _kittenDataSource.count < kMaxLitterSize; +} + - (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 +- (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]; + [_tableNode deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } } diff --git a/examples/SocialAppLayout/Sample/ViewController.h b/examples/SocialAppLayout/Sample/ViewController.h index 40359e2f0e..bfb359c6b5 100644 --- a/examples/SocialAppLayout/Sample/ViewController.h +++ b/examples/SocialAppLayout/Sample/ViewController.h @@ -15,7 +15,7 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import +#import -@interface ViewController : UIViewController +@interface ViewController : ASViewController @end diff --git a/examples/SocialAppLayout/Sample/ViewController.m b/examples/SocialAppLayout/Sample/ViewController.m index 492861c01b..71458a1ae8 100644 --- a/examples/SocialAppLayout/Sample/ViewController.m +++ b/examples/SocialAppLayout/Sample/ViewController.m @@ -24,45 +24,47 @@ #include -@interface ViewController () +@interface ViewController () -@property (nonatomic, strong) ASTableView *tableView; +@property (nonatomic, strong) ASTableNode *tableNode; @property (nonatomic, strong) NSMutableArray *socialAppDataSource; @end +#pragma mark - Lifecycle @implementation ViewController - (instancetype)init { - self = [super init]; + _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + + self = [super initWithNode:_tableNode]; + if (self) { + + _tableNode.delegate = self; + _tableNode.dataSource = self; + _tableNode.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.title = @"Timeline"; + [self createSocialAppDataSource]; } + return self; } - -- (void)dealloc -{ - _tableView.asyncDataSource = nil; - _tableView.asyncDelegate = nil; -} - - (void)viewDidLoad { [super viewDidLoad]; - - self.tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; - self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // SocialAppNode has its own separator - self.tableView.asyncDataSource = self; - self.tableView.asyncDelegate = self; - [self.view addSubview:self.tableView]; + + // SocialAppNode has its own separator + self.tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone; } +#pragma mark - Data Model + - (void)createSocialAppDataSource { _socialAppDataSource = [[NSMutableArray alloc] init]; @@ -116,9 +118,9 @@ [_socialAppDataSource addObject:newPost]; } -#pragma mark - ASTableView +#pragma mark - ASTableNode -- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(nonnull NSIndexPath *)indexPath +- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { Post *post = self.socialAppDataSource[indexPath.row]; return ^{ @@ -126,14 +128,14 @@ }; } -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { return self.socialAppDataSource.count; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - PostNode *postNode = (PostNode *)[_tableView nodeForRowAtIndexPath:indexPath]; + PostNode *postNode = (PostNode *)[_tableNode nodeForRowAtIndexPath:indexPath]; Post *post = self.socialAppDataSource[indexPath.row]; BOOL shouldRasterize = postNode.shouldRasterizeDescendants; @@ -142,7 +144,7 @@ NSLog(@"%@ rasterization for %@'s post: %@", shouldRasterize ? @"Enabling" : @"Disabling", post.name, postNode); - [tableView deselectRowAtIndexPath:indexPath animated:YES]; + [tableNode deselectRowAtIndexPath:indexPath animated:YES]; } @end diff --git a/examples/Swift/Sample/ViewController.swift b/examples/Swift/Sample/ViewController.swift index 0982081c0a..66ed35391b 100644 --- a/examples/Swift/Sample/ViewController.swift +++ b/examples/Swift/Sample/ViewController.swift @@ -47,12 +47,12 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate fatalError("storyboards are incompatible with truth and beauty") } - // MARK: ASTableView data source and delegate. + // MARK: ASTableNode data source and delegate. - func tableView(tableView: ASTableView, nodeForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNode { + func tableNode(tableNode: ASTableNode, nodeForRowAtIndexPath indexPath: NSIndexPath) -> ASCellNode { // Should read the row count directly from table view but // https://github.com/facebook/AsyncDisplayKit/issues/1159 - let rowCount = self.tableView(tableView, numberOfRowsInSection: 0) + let rowCount = self.tableNode(tableNode, numberOfRowsInSection: 0) if state.fetchingMore && indexPath.row == rowCount - 1 { return TailLoadingCellNode() @@ -64,11 +64,11 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate return node } - func numberOfSectionsInTableView(tableView: UITableView) -> Int { + func numberOfSectionsInTableNode(tableNode: ASTableNode) -> Int { return 1 } - func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + func tableNode(tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int { var count = state.itemCount if state.fetchingMore { count += 1 @@ -76,7 +76,7 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate return count } - func tableView(tableView: ASTableView, willBeginBatchFetchWithContext context: ASBatchContext) { + func tableNode(tableNode: ASTableNode, willBeginBatchFetchWithContext context: ASBatchContext) { /// This call will come in on a background thread. Switch to main /// to add our spinner, then fire off our fetch. dispatch_async(dispatch_get_main_queue()) { @@ -95,33 +95,33 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate } private func renderDiff(oldState: State) { - let tableView = tableNode.view - tableView.beginUpdates() - - // Add or remove items - let rowCountChange = state.itemCount - oldState.itemCount - if rowCountChange > 0 { - let indexPaths = (oldState.itemCount.. 0 { + let indexPaths = (oldState.itemCount.. +@interface GradientTableNode : ASCellNode - (instancetype)initWithElementSize:(CGSize)size; diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm b/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm index 907fa16a4e..8e6c884f11 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm +++ b/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm @@ -24,7 +24,8 @@ #import #import -@interface GradientTableNode () + +@interface GradientTableNode () { ASTableNode *_tableNode; CGSize _elementSize; @@ -43,40 +44,37 @@ _elementSize = size; _tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + _tableNode.delegate = self; + _tableNode.dataSource = self; + + ASRangeTuningParameters rangeTuningParameters; + rangeTuningParameters.leadingBufferScreenfuls = 1.0; + rangeTuningParameters.trailingBufferScreenfuls = 0.5; + [_tableNode setTuningParameters:rangeTuningParameters forRangeType:ASLayoutRangeTypeDisplay]; + [self addSubnode:_tableNode]; return self; } -- (void)didLoad -{ - [super didLoad]; - _tableNode.view.asyncDelegate = self; - _tableNode.view.asyncDataSource = self; - - ASRangeTuningParameters rangeTuningParameters; - rangeTuningParameters.leadingBufferScreenfuls = 1.0; - rangeTuningParameters.trailingBufferScreenfuls = 0.5; - [_tableNode.view setTuningParameters:rangeTuningParameters forRangeType:ASLayoutRangeTypeDisplay]; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { return 100; } -- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath { RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; elementNode.style.preferredSize = _elementSize; elementNode.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:_pageNumber]; + return elementNode; } -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - [tableView deselectRowAtIndexPath:indexPath animated:NO]; - [_tableNode.view reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; + [tableNode deselectRowAtIndexPath:indexPath animated:NO]; + [_tableNode reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone]; } - (void)layout diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m index 74dd517084..16d40df7aa 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m +++ b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m @@ -22,6 +22,8 @@ @implementation RandomCoreGraphicsNode +@synthesize indexPath=_indexPath; + + (UIColor *)randomColor { CGFloat hue = ( arc4random() % 256 / 256.0 ); // 0.0 to 1.0