From cefbcef8311335092b5cf4dcfdd42a41476ff1c9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 19:44:24 -0700 Subject: [PATCH 01/68] Documentation updates --- AsyncDisplayKit/Details/ASDataController.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 589d210f9d..ebb725bfb2 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -90,7 +90,6 @@ typedef NSUInteger ASDataControllerAnimationOptions; @end - /** * Controller to layout data in background, and managed data updating. * @@ -177,6 +176,9 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths; -- (NSArray *)completedNodes; // This provides efficient access to the entire _completedNodes multidimensional array. +/** + * Direct access to the nodes that have completed calculation and layout + */ +- (NSArray *)completedNodes; @end From 2956c0cd8a72e95fb76799892ed935a66c89db04 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 20:43:18 -0700 Subject: [PATCH 02/68] Extract flow layout methods into a separate delegate --- AsyncDisplayKit/ASCollectionView.h | 20 +++++++++++++++++-- AsyncDisplayKit/ASCollectionView.mm | 2 +- .../ASCollectionView/Sample/ViewController.m | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index d8f78c6e13..4b2048f6ec 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -342,18 +342,34 @@ */ - (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView; +@end + +@protocol ASCollectionViewDelegateFlowLayout + +@optional + /** * Passthrough support to UICollectionViewDelegateFlowLayout sectionInset behavior. * * @param collectionView The sender. * @param collectionViewLayout The layout object requesting the information. - * #param section The index number of the section whose insets are needed. + * @param section The index number of the section whose insets are needed. * * @discussion The same rules apply as the UICollectionView implementation, but this can also be used without a UICollectionViewFlowLayout. * https://developer.apple.com/library/ios/documentation/UIKit/Reference/UICollectionViewDelegateFlowLayout_protocol/index.html#//apple_ref/occ/intfm/UICollectionViewDelegateFlowLayout/collectionView:layout:insetForSectionAtIndex: * */ -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section; + +/** + * Asks the delegate for the size of the header in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section; + +/** + * Asks the delegate for the size of the footer in the specified section. + */ +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section; @end diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 3c1cc4dc18..d2288f39e6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -628,7 +628,7 @@ static BOOL _isInterceptedSelector(SEL sel) } if (_asyncDelegateImplementsInsetSection) { - sectionInset = [_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; + sectionInset = [(id)_asyncDelegate collectionView:self layout:self.collectionViewLayout insetForSectionAtIndex:indexPath.section]; } if (ASScrollDirectionContainsHorizontalDirection([self scrollableDirections])) { diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index f87e26f9d7..a6ceb20f4d 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -13,7 +13,7 @@ #import -@interface ViewController () +@interface ViewController () { ASCollectionView *_collectionView; } @@ -95,7 +95,7 @@ [context completeBatchFetching:YES]; } -- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { +- (UIEdgeInsets)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(20.0, 20.0, 20.0, 20.0); } From 4e32c075d499720a79bf25e4e1c18d0ba7428931 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 20:45:10 -0700 Subject: [PATCH 03/68] Add launch storyboard to collection view example to fix screen size --- examples/ASCollectionView/Sample.xcodeproj/project.pbxproj | 4 ++++ examples/ASCollectionView/Sample/Info.plist | 2 ++ examples/ASCollectionView/Sample/Launchboard.storyboard | 7 +++++++ 3 files changed, 13 insertions(+) create mode 100644 examples/ASCollectionView/Sample/Launchboard.storyboard diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 7f64fc5596..03c3323afb 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; settings = {ASSET_TAGS = (); }; }; AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; }; AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; }; AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A691A11F47200143C57 /* ViewController.m */; }; @@ -16,6 +17,7 @@ /* Begin PBXFileReference section */ 2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = ""; }; AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AC3C4A631A11F47200143C57 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -88,6 +90,7 @@ children = ( AC3C4A621A11F47200143C57 /* Info.plist */, AC3C4A631A11F47200143C57 /* main.m */, + 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */, ); name = "Supporting Files"; sourceTree = ""; @@ -159,6 +162,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */, AC3C4A8E1A11F80C00143C57 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/examples/ASCollectionView/Sample/Info.plist b/examples/ASCollectionView/Sample/Info.plist index a3664b0b15..eeb71a8d35 100644 --- a/examples/ASCollectionView/Sample/Info.plist +++ b/examples/ASCollectionView/Sample/Info.plist @@ -26,6 +26,8 @@ 1 LSRequiresIPhoneOS + UILaunchStoryboardName + Launchboard UIRequiredDeviceCapabilities armv7 diff --git a/examples/ASCollectionView/Sample/Launchboard.storyboard b/examples/ASCollectionView/Sample/Launchboard.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/examples/ASCollectionView/Sample/Launchboard.storyboard @@ -0,0 +1,7 @@ + + + + + + + From 0d2332bd82ef3e97e7a5ca5fe35f305e6e9ec7d7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 21:03:49 -0700 Subject: [PATCH 04/68] Allow ASCollectionView to register for supplementary container --- AsyncDisplayKit/ASCollectionView.h | 2 ++ AsyncDisplayKit/ASCollectionView.mm | 6 ++++++ examples/ASCollectionView/Sample/ViewController.m | 3 +++ 3 files changed, 11 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 4b2048f6ec..a5b0aae9c7 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -119,6 +119,8 @@ */ - (void)reloadData; +- (void)registerSupplementaryViewOfKind:(NSString *)elementKind; + /** * Inserts one or more sections. * diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index d2288f39e6..8148e55d7b 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -379,6 +379,12 @@ static BOOL _isInterceptedSelector(SEL sel) [self performBatchAnimated:YES updates:updates completion:completion]; } +- (void)registerSupplementaryViewOfKind:(NSString *)elementKind +{ + NSString *identifier = [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", elementKind]; + [self registerClass:[UIView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier]; +} + - (void)insertSections:(NSIndexSet *)sections { ASDisplayNodeAssertMainThread(); diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index a6ceb20f4d..779f4e6666 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -39,6 +39,9 @@ _collectionView.asyncDelegate = self; _collectionView.backgroundColor = [UIColor whiteColor]; + [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionHeader]; + [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionFooter]; + return self; } From e492770aed2cc2eccfc20557921deb297772ca27 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 21:08:50 -0700 Subject: [PATCH 05/68] Expose collection view supplementary node data source method --- AsyncDisplayKit/ASCollectionView.h | 2 ++ examples/ASCollectionView/Sample/ViewController.m | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index a5b0aae9c7..7d3ea56fc9 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -274,6 +274,8 @@ @optional +- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + /** * Provides the constrained size range for measuring the node at the index path. * diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 779f4e6666..7e97f9bb53 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -76,6 +76,17 @@ return node; } +- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNode *node = [[ASDisplayNode alloc] init]; + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + node.backgroundColor = [UIColor blueColor]; + } else { + node.backgroundColor = [UIColor redColor]; + } + return node; +} + - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 300; From e9eadac4aee180f76ccadbe86d54ec4219249b38 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 22 Sep 2015 23:41:43 -0700 Subject: [PATCH 06/68] Stub out ASCollectionDataController subclass --- AsyncDisplayKit/ASCollectionView.mm | 43 ++++++++--- AsyncDisplayKit/ASTableView.mm | 4 +- .../Details/ASCollectionDataController.h | 26 +++++++ .../Details/ASCollectionDataController.m | 72 +++++++++++++++++++ AsyncDisplayKit/Details/ASDataController.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 6 +- AsyncDisplayKit/Details/ASRangeController.h | 4 +- AsyncDisplayKit/Details/ASRangeController.mm | 4 +- .../Details/ASRangeHandlerRender.mm | 2 +- 9 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASCollectionDataController.h create mode 100644 AsyncDisplayKit/Details/ASCollectionDataController.m diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8148e55d7b..4d52ef5e19 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -11,7 +11,7 @@ #import "ASAssert.h" #import "ASCollectionViewLayoutController.h" #import "ASRangeController.h" -#import "ASDataController.h" +#import "ASCollectionDataController.h" #import "ASDisplayNodeInternal.h" #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" @@ -37,9 +37,7 @@ static BOOL _isInterceptedSelector(SEL sel) // handled by ASCollectionView node<->cell machinery sel == @selector(collectionView:cellForItemAtIndexPath:) || sel == @selector(collectionView:layout:sizeForItemAtIndexPath:) || - - // TODO: Supplementary views are currently not supported. An assertion is triggered if the _asyncDataSource implements this method. - // sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || + sel == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) || // handled by ASRangeController sel == @selector(numberOfSectionsInCollectionView:) || @@ -136,7 +134,7 @@ static BOOL _isInterceptedSelector(SEL sel) _ASCollectionViewProxy *_proxyDataSource; _ASCollectionViewProxy *_proxyDelegate; - ASDataController *_dataController; + ASCollectionDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; @@ -201,7 +199,7 @@ static BOOL _isInterceptedSelector(SEL sel) _rangeController.delegate = self; _rangeController.layoutController = _layoutController; - _dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; + _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.delegate = _rangeController; _dataController.dataSource = self; @@ -381,8 +379,8 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryViewOfKind:(NSString *)elementKind { - NSString *identifier = [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", elementKind]; - [self registerClass:[UIView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier]; + [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind + withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; } - (void)insertSections:(NSIndexSet *)sections @@ -433,6 +431,16 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } +- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [_dataController nodeAtIndexPath:indexPath]; +} + +- (NSString *)__reuseIdentifierForKind:(NSString *)kind +{ + return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; +} + #pragma mark - #pragma mark Intercepted selectors. @@ -443,7 +451,8 @@ static BOOL _isInterceptedSelector(SEL sel) _ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forCellNode:node]; + + [_rangeController configureContentView:cell.contentView forNode:node]; cell.node = node; @@ -455,6 +464,15 @@ static BOOL _isInterceptedSelector(SEL sel) return [[_dataController nodeAtIndexPath:indexPath] calculatedSize]; } +- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + NSString *identifier = [self __reuseIdentifierForKind:kind]; + UICollectionReusableView *view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:identifier forIndexPath:indexPath]; + ASDisplayNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; + [_rangeController configureContentView:view forNode:node]; + return view; +} + - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { _superIsPendingDataLoad = NO; @@ -613,6 +631,11 @@ static BOOL _isInterceptedSelector(SEL sel) return node; } +- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +} + - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { ASSizeRange constrainedSize; @@ -659,7 +682,7 @@ static BOOL _isInterceptedSelector(SEL sel) return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; } -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController { +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [_asyncDataSource numberOfSectionsInCollectionView:self]; } else { diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 20c058d658..a8dc8c6f9d 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -530,7 +530,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forCellNode:node]; + [_rangeController configureContentView:cell.contentView forNode:node]; cell.node = node; cell.backgroundColor = node.backgroundColor; @@ -859,7 +859,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { return [_asyncDataSource tableView:self numberOfRowsInSection:section]; } -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) { return [_asyncDataSource numberOfSectionsInTableView:self]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h new file mode 100644 index 0000000000..c623dfbbcf --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -0,0 +1,26 @@ +// +// ASCollectionDataController.h +// Pods +// +// Created by Levi McCallum on 9/22/15. +// +// + +#import + +@class ASDisplayNode, ASCollectionDataController; +@protocol ASDataControllerSource; + +@protocol ASCollectionDataControllerSource + +- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; + +@end + +@interface ASCollectionDataController : ASDataController + +- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +@end \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.m new file mode 100644 index 0000000000..cf772c3b67 --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionDataController.m @@ -0,0 +1,72 @@ +// +// ASCollectionDataController.m +// Pods +// +// Created by Levi McCallum on 9/22/15. +// +// + +#import "ASCollectionDataController.h" + +#import "ASAssert.h" + +@interface ASDataController (Subclasses) + +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ +- (void)performEditCommandWithBlock:(void (^)(void))block; + +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ +- (void)accessDataSourceWithBlock:(dispatch_block_t)block; + +@end + +@implementation ASCollectionDataController { + NSMutableDictionary *_completedSupplementaryNodes; + NSMutableDictionary *_editingSupplementaryNodes; +} + +- (void)initialSupplementaryLoading +{ + [self performEditCommandWithBlock:^{ + ASDisplayNodeAssertMainThread(); + [self accessDataSourceWithBlock:^{ + NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + }]; + }]; +} + +- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + return _completedSupplementaryNodes[kind][indexPath.section][indexPath.item]; +} + +- (id)collectionDataSource +{ + return (id)self.dataSource; +} + +#pragma mark - Internal Data Querying + +- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths +{ + if (indexPaths.count == 0) + return; +} + +- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths +{ + if (indexPaths.count == 0) + return; +} + +@end diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index ebb725bfb2..39d0b78792 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -40,7 +40,7 @@ typedef NSUInteger ASDataControllerAnimationOptions; /** Fetch the number of sections. */ -- (NSUInteger)dataControllerNumberOfSections:(ASDataController *)dataController; +- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController; /** Lock the data source for data fetching. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 6a5b361ff0..a963f44ae2 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -239,7 +239,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ NSMutableArray *indexPaths = [NSMutableArray array]; - NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; // insert sections [self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:0]; @@ -266,7 +266,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - NSUInteger sectionCount = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self]; NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -335,7 +335,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)_populateFromEntireDataSourceWithMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { - NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self]; + NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; for (NSUInteger i = 0; i < sectionNum; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index 9d5bbe57f0..a7fc3e54df 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -41,9 +41,9 @@ * * @param contentView UIView to add a (sized) node's view to. * - * @param node The ASCellNode to be added. + * @param node The node to be added. Often an ASCellNode. */ -- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node; /** * Delegate and ultimate data source. Must not be nil. diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index b22db853bf..985e6f60cc 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -48,7 +48,7 @@ #pragma mark - View manipulation -- (void)moveNode:(ASCellNode *)node toView:(UIView *)view +- (void)moveNode:(ASDisplayNode *)node toView:(UIView *)view { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); @@ -158,7 +158,7 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)cellNode +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)cellNode { if (cellNode.view.superview == contentView) { // this content view is already correctly configured diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 0363abba2f..0b6efd8568 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -64,7 +64,7 @@ // This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not // proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling // -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared - // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:]. + // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forNode:]. // Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the // preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards. // Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality. From ed1b737ead2ef0e795667ed13bebd70c5a4ae4c7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 23 Sep 2015 07:53:03 -0700 Subject: [PATCH 07/68] Define initial supplementary node creation behavior --- AsyncDisplayKit-Prefix.gcda | Bin 0 -> 656 bytes AsyncDisplayKit/ASTableView.mm | 16 ++-- .../Details/ASCollectionDataController.h | 24 ++++-- .../Details/ASCollectionDataController.m | 77 ++++++++++++------ .../Details/ASDataController+Subclasses.h | 30 +++++++ AsyncDisplayKit/Details/ASDataController.mm | 71 ++++++++-------- Base/ASLog.h | 3 + 7 files changed, 146 insertions(+), 75 deletions(-) create mode 100644 AsyncDisplayKit-Prefix.gcda create mode 100644 AsyncDisplayKit/Details/ASDataController+Subclasses.h diff --git a/AsyncDisplayKit-Prefix.gcda b/AsyncDisplayKit-Prefix.gcda new file mode 100644 index 0000000000000000000000000000000000000000..450c9162de2c4e70a2d89835d0e42d57404c1766 GIT binary patch literal 656 zcmbV|&q@O^5XM^tPogLaf*0{1s0U4}Al?>Tp|q5;sR)V;+3d7kU6Lh9mGvTsuOWEw zLXs(Hw)aZ)|#jS=~_e-)6;bN1NZgt_tA!b0p)mH;x`ZeD;z2 zo{;BhR`6k5^xHy&D#0N(ogQ>WxItwY)fuCCrdJpV)kXT6qvT}eRTAId-BY~0LJr4G z?z6ESsr$-7+Nm`;Fa~gGL=LG#Q)|@^cL1!T&bqC`N>#^aGB?_3Rtfm`JVl%#WBvz_ zpmbr_I*54{!Z7r_Z4VMo8*oOX&eqxxvh5`8bkLxZ2(HL6 #import +#import -@class ASDisplayNode, ASCollectionDataController; +@class ASDisplayNode; +@class ASCollectionDataController; @protocol ASDataControllerSource; @protocol ASCollectionDataControllerSource @@ -17,6 +21,10 @@ - (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController rowsInSection:(NSUInteger)section supplementaryKind:(NSString *)kind; + @end @interface ASCollectionDataController : ASDataController diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.m index cf772c3b67..8863d0b3b9 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.m +++ b/AsyncDisplayKit/Details/ASCollectionDataController.m @@ -1,33 +1,18 @@ -// -// ASCollectionDataController.m -// Pods -// -// Created by Levi McCallum on 9/22/15. -// -// +/* Copyright (c) 2015-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 "ASCollectionDataController.h" #import "ASAssert.h" - -@interface ASDataController (Subclasses) - -/** - * Queues the given operation until an `endUpdates` synchronize update is completed. - * - * If this method is called outside of a begin/endUpdates batch update, the block is - * executed immediately. - */ -- (void)performEditCommandWithBlock:(void (^)(void))block; - -/** - * Safely locks access to the data source and executes the given block, unlocking once complete. - * - * When `asyncDataFetching` is enabled, the block is executed on a background thread. - */ -- (void)accessDataSourceWithBlock:(dispatch_block_t)block; - -@end +#import "ASMultidimensionalArrayUtils.h" +#import "ASDisplayNode.h" +#import "ASDisplayNodeInternal.h" +#import "ASDataController+Subclasses.h" @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; @@ -40,9 +25,32 @@ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + _completedSupplementaryNodes[kind] = [NSMutableArray array]; + _editingSupplementaryNodes[kind] = [NSMutableArray array]; + + NSMutableArray *indexPaths = [NSMutableArray array]; + NSMutableArray *nodes = [NSMutableArray array]; + [self _populateAllNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:nil]; + }]; }]; }]; } + +- (void)_populateAllNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +{ + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + for (NSUInteger i = 0; i < sectionCount; i++) { + NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; + NSUInteger rowCount = [self.collectionDataSource dataController:self rowsInSection:i supplementaryKind:kind]; + for (NSUInteger j = 0; j < rowCount; j++) { + NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; + [indexPaths addObject:indexPath]; + [nodes addObject:[self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath]]; + } + } +} - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { @@ -61,6 +69,13 @@ { if (indexPaths.count == 0) return; + NSMutableArray *editingNodes = [self _editingNodesOfKind:kind]; + ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); + NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); + ASDisplayNodePerformBlockOnMainThread(^{ + _completedSupplementaryNodes[kind] = completedNodes; + // TODO: Notify change + }); } - (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths @@ -69,4 +84,14 @@ return; } +- (NSMutableArray *)_completedNodesOfKind:(NSString *)kind +{ + return _completedSupplementaryNodes[kind]; +} + +- (NSMutableArray *)_editingNodesOfKind:(NSString *)kind +{ + return _editingSupplementaryNodes[kind]; +} + @end diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h new file mode 100644 index 0000000000..1e14ccc6eb --- /dev/null +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2015-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 "ASDataController.h" + +@interface ASDataController (Subclasses) + +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ +- (void)performEditCommandWithBlock:(void (^)(void))block; + +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ +- (void)accessDataSourceWithBlock:(dispatch_block_t)block; + +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block; + +@end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index a963f44ae2..ed79598d55 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -10,15 +10,13 @@ #import +#import "ASLog.h" #import "ASAssert.h" #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNodeInternal.h" -//#define LOG(...) NSLog(__VA_ARGS__) -#define LOG(...) - const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; static void *kASSizingQueueContext = &kASSizingQueueContext; @@ -115,7 +113,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *, NSArray *))block { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -152,11 +150,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Block the _editingTransactionQueue from executing a new edit transaction until layout is done & _editingNodes array is updated. dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - // Insert finished nodes into data storage - [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + + block(nodes, indexPaths); } -- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -166,10 +164,19 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOptions:animationOptions]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths completion:block]; } } +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + + [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + // Insert finished nodes into data storage + [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + }]; +} + #pragma mark - Internal Data Querying + Editing - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions @@ -192,7 +199,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - LOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); ASDisplayNodePerformBlockOnMainThread(^{ @@ -275,7 +282,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - reloadData"); + ASLOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes); @@ -369,7 +376,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _batchUpdateCounter--; if (_batchUpdateCounter == 0) { - LOG(@"endUpdatesWithCompletion - beginning"); + ASLOG(@"endUpdatesWithCompletion - beginning"); [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ @@ -377,16 +384,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes); - LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); + ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; }); }]; // Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates. // Each subsequent command in the queue will also wait on the full asynchronous completion of the prior command's edit transaction. - LOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); + ASLOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); [_pendingEditCommandBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { - LOG(@"endUpdatesWithCompletion - running block #%zd", idx); + ASLOG(@"endUpdatesWithCompletion - running block #%zd", idx); block(); }]; [_pendingEditCommandBlocks removeAllObjects]; @@ -396,7 +403,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Now that the transaction is done, _completedNodes can be accessed externally again. _externalCompletedNodes = nil; - LOG(@"endUpdatesWithCompletion - calling delegate end"); + ASLOG(@"endUpdatesWithCompletion - calling delegate end"); [_delegate dataController:self endUpdatesAnimated:animated completion:completion]; }); }]; @@ -420,7 +427,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertSections: %@", indexSet); + ASLOG(@"Edit Command - insertSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -432,7 +439,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - insertSections: %@", indexSet); + ASLOG(@"Edit Transaction - insertSections: %@", indexSet); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; for (NSUInteger i = 0; i < indexSet.count; i++) { [sectionArray addObject:[NSMutableArray array]]; @@ -449,12 +456,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteSections: %@", indexSet); + ASLOG(@"Edit Command - deleteSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - LOG(@"Edit Transaction - deleteSections: %@", indexSet); + ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -467,7 +474,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - reloadSections: %@", sections); + ASLOG(@"Edit Command - reloadSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -486,7 +493,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections); - LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -501,14 +508,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - moveSection"); + ASLOG(@"Edit Command - moveSection"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - LOG(@"Edit Transaction - moveSection"); + ASLOG(@"Edit Transaction - moveSection"); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); @@ -533,7 +540,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertRows: %@", indexPaths); + ASLOG(@"Edit Command - insertRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -549,7 +556,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - insertRows: %@", indexPaths); + ASLOG(@"Edit Transaction - insertRows: %@", indexPaths); [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -560,7 +567,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteRows: %@", indexPaths); + ASLOG(@"Edit Command - deleteRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -569,7 +576,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - deleteRows: %@", indexPaths); + ASLOG(@"Edit Transaction - deleteRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -579,7 +586,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - reloadRows: %@", indexPaths); + ASLOG(@"Edit Command - reloadRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -599,7 +606,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - reloadRows: %@", indexPaths); + ASLOG(@"Edit Transaction - reloadRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -611,7 +618,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - relayoutRows"); + ASLOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { @@ -646,11 +653,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); + ASLOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ - LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); + ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; diff --git a/Base/ASLog.h b/Base/ASLog.h index 4c85bdf99a..c2f3de15c4 100644 --- a/Base/ASLog.h +++ b/Base/ASLog.h @@ -8,6 +8,9 @@ #pragma once +//#define ASLOG(...) NSLog(__VA_ARGS__) +#define ASLOG(...) + #define ASMultiplexImageNodeLogDebug(...) #define ASMultiplexImageNodeCLogDebug(...) From 658b78d55215417605e6491dbaa52ac43faac7d3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 26 Sep 2015 10:30:24 -0700 Subject: [PATCH 08/68] Use objective-c++ for subclass --- ...ASCollectionDataController.m => ASCollectionDataController.mm} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename AsyncDisplayKit/Details/{ASCollectionDataController.m => ASCollectionDataController.mm} (100%) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.m b/AsyncDisplayKit/Details/ASCollectionDataController.mm similarity index 100% rename from AsyncDisplayKit/Details/ASCollectionDataController.m rename to AsyncDisplayKit/Details/ASCollectionDataController.mm From da7a2a5d481f18d73a9770e7638c3add6422b7e3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 08:59:40 -0700 Subject: [PATCH 09/68] Further implement data controller support and layout introspection --- AsyncDisplayKit/ASCollectionView.h | 11 +++++-- AsyncDisplayKit/ASCollectionView.mm | 15 +++++++-- .../Details/ASCollectionDataController.h | 9 +++-- .../Details/ASCollectionDataController.mm | 18 +++++++--- .../ASCollectionViewFlowLayoutInspector.h | 31 +++++++++++++++++ .../ASCollectionViewFlowLayoutInspector.m | 33 +++++++++++++++++++ .../Details/ASDataController+Subclasses.h | 5 ++- AsyncDisplayKit/Details/ASDataController.mm | 15 +++++---- .../ASCollectionView/Sample/ViewController.m | 4 +-- 9 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h create mode 100644 AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 7d3ea56fc9..91226a5d64 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -17,7 +17,7 @@ @class ASCellNode; @protocol ASCollectionViewDataSource; @protocol ASCollectionViewDelegate; - +@protocol ASCollectionViewLayoutInspecting; /** * Node-based collection view. @@ -80,6 +80,13 @@ */ @property (nonatomic, assign) CGFloat leadingScreensForBatching; +/** + * Optional introspection object for the collection view's layout. + * + * TODO: Discuss more about this delegate + */ +@property (nonatomic, weak) id layoutDelegate; + /** * Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread. * The asyncDataSource must be updated to reflect the changes before the update block completes. @@ -119,7 +126,7 @@ */ - (void)reloadData; -- (void)registerSupplementaryViewOfKind:(NSString *)elementKind; +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind; /** * Inserts one or more sections. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 4d52ef5e19..fb9dbeae1a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -16,6 +16,7 @@ #import "ASBatchFetching.h" #import "UICollectionViewLayout+ASConvenience.h" #import "ASInternalHelpers.h" +#import "ASCollectionViewFlowLayoutInspector.h" // FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASDisplayNode+Subclasses.h" @@ -137,6 +138,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASCollectionDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; + ASCollectionViewFlowLayoutInspector *_flowLayoutInspector; BOOL _performingBatchUpdates; NSMutableArray *_batchUpdateBlocks; @@ -202,7 +204,11 @@ static BOOL _isInterceptedSelector(SEL sel) _dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController.delegate = _rangeController; _dataController.dataSource = self; - + + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; + // TODO: Implement a better path of falling-back to a flow layout + _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; + _batchContext = [[ASBatchContext alloc] init]; _leadingScreensForBatching = 1.0; @@ -377,7 +383,7 @@ static BOOL _isInterceptedSelector(SEL sel) [self performBatchAnimated:YES updates:updates completion:completion]; } -- (void)registerSupplementaryViewOfKind:(NSString *)elementKind +- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; @@ -677,6 +683,11 @@ static BOOL _isInterceptedSelector(SEL sel) return constrainedSize; } +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; +} + - (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section { return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index c938955608..fddab0eb46 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -17,9 +17,14 @@ @protocol ASCollectionDataControllerSource -- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; -- (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController; +/** + The constrained size range for layout. + */ +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 8863d0b3b9..243b1b4612 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -14,6 +14,12 @@ #import "ASDisplayNodeInternal.h" #import "ASDataController+Subclasses.h" +@interface ASCollectionDataController () + +- (id)collectionDataSource; + +@end + @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; NSMutableDictionary *_editingSupplementaryNodes; @@ -24,21 +30,23 @@ [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ - NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { _completedSupplementaryNodes[kind] = [NSMutableArray array]; _editingSupplementaryNodes[kind] = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; - [self _populateAllNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; - [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:nil]; + [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } completion:nil]; }]; }]; }]; } -- (void)_populateAllNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { @@ -65,6 +73,7 @@ #pragma mark - Internal Data Querying +// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController - (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { if (indexPaths.count == 0) @@ -78,6 +87,7 @@ }); } +// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController - (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { if (indexPaths.count == 0) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h new file mode 100644 index 0000000000..911a000e5c --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -0,0 +1,31 @@ +// +// ASCollectionViewFlowLayoutInspector.h +// Pods +// +// Created by Levi McCallum on 9/29/15. +// +// + +#import + +#import + +@class ASCollectionView; + +@protocol ASCollectionViewLayoutInspecting + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; + +@end + +@interface ASCollectionViewFlowLayoutInspector : NSObject + +@property (nonatomic, weak) UICollectionViewFlowLayout *layout; + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; + +@end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m new file mode 100644 index 0000000000..274f576c61 --- /dev/null +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -0,0 +1,33 @@ +// +// ASCollectionViewFlowLayoutInspector.m +// Pods +// +// Created by Levi McCallum on 9/29/15. +// +// + +#import "ASCollectionViewFlowLayoutInspector.h" + +#import "ASCollectionView.h" + +@implementation ASCollectionViewFlowLayoutInspector + +- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + // TODO: Implement some heuristic that follows the width/height constraints of header and footer supplementary views + return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); +} + +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + NSUInteger count = 0; + if (self.layout.headerReferenceSize.width > 0 || self.layout.headerReferenceSize.height > 0) { + count++; + } + if (self.layout.footerReferenceSize.width > 0 || self.layout.footerReferenceSize.height > 0) { + count++; + } + return count; +} + +@end diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1e14ccc6eb..e07bb5d031 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,6 +25,9 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block; +/** + * Measure and layout the given nodes in optimized batches, constraining each to a given size. + */ +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index ed79598d55..844e61706b 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -113,7 +113,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *, NSArray *))block +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -129,7 +129,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[k]]; + nodeBoundSizes[k] = constraintedSizeBlock(indexPaths[k]); } } @@ -151,10 +151,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - block(nodes, indexPaths); + completionBlock(nodes, indexPaths); } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -164,14 +164,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths completion:block]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; } } - (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - - [self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { // Insert finished nodes into data storage [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 7e97f9bb53..885ef3e00f 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -39,8 +39,8 @@ _collectionView.asyncDelegate = self; _collectionView.backgroundColor = [UIColor whiteColor]; - [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionHeader]; - [_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionFooter]; + [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; + [_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter]; return self; } From 5dee3f62cc03e9e584017c067e6c9a7158f2032b Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 09:14:22 -0700 Subject: [PATCH 10/68] Extract number of supplementary sections into the inspector --- AsyncDisplayKit/ASCollectionView.mm | 14 ++++++++++++-- .../Details/ASCollectionDataController.h | 2 +- .../Details/ASCollectionDataController.mm | 2 +- .../Details/ASCollectionViewFlowLayoutInspector.h | 5 +++++ .../Details/ASCollectionViewFlowLayoutInspector.m | 4 ++++ 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index fb9dbeae1a..2b1977bba8 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -683,14 +683,24 @@ static BOOL _isInterceptedSelector(SEL sel) return constrainedSize; } +- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +{ + return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; +} + - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } -- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; + return [self.layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +{ + return [self.layoutDelegate collectionView:self numerOfSectionsForSupplementaryKind:kind]; } - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index fddab0eb46..08f8775270 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -28,7 +28,7 @@ - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController rowsInSection:(NSUInteger)section supplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 243b1b4612..1fa63a4999 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -51,7 +51,7 @@ NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; - NSUInteger rowCount = [self.collectionDataSource dataController:self rowsInSection:i supplementaryKind:kind]; + NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:i]; for (NSUInteger j = 0; j < rowCount; j++) { NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; [indexPaths addObject:indexPath]; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 911a000e5c..e28ff0878f 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -16,16 +16,21 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end @interface ASCollectionViewFlowLayoutInspector : NSObject +@property (nonatomic, weak) ASCollectionView *collectionView; @property (nonatomic, weak) UICollectionViewFlowLayout *layout; - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 274f576c61..0a0b1598dc 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -18,6 +18,10 @@ return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); } +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { + return self.collectionView.numberOfSections; +} + - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { NSUInteger count = 0; From d76544420b3a1df5057e91c4d1d9bb655a2ee3a4 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 14:43:26 -0700 Subject: [PATCH 11/68] Add new files to xcodeproj --- AsyncDisplayKit.xcodeproj/project.pbxproj | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 1a58fabfe9..7c0956d4d3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -140,6 +140,11 @@ 205F0E211B376416007741D0 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; }; + 251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; settings = {ASSET_TAGS = (); }; }; + 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; settings = {ASSET_TAGS = (); }; }; + 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ASSET_TAGS = (); }; }; + 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; settings = {ASSET_TAGS = (); }; }; + 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; settings = {ASSET_TAGS = (); }; }; 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; settings = {ASSET_TAGS = (); }; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; @@ -544,6 +549,11 @@ 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CGRect+ASConvenience.h"; sourceTree = ""; }; 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CGRect+ASConvenience.m"; sourceTree = ""; }; 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = ""; }; + 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionDataController.h; sourceTree = ""; }; + 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionDataController.mm; sourceTree = ""; }; + 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; + 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = ""; }; + 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = ""; }; @@ -847,6 +857,11 @@ children = ( CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */, CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */, + 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */, + 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */, + 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */, + 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */, + 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */, 058D09E2195D050800B7D73C /* _ASDisplayLayer.h */, 058D09E3195D050800B7D73C /* _ASDisplayLayer.mm */, 058D09E4195D050800B7D73C /* _ASDisplayView.h */, @@ -1062,6 +1077,7 @@ 054963491A1EA066000F8E56 /* ASBasicImageDownloader.h in Headers */, 2967F9E21AB0A5190072E4AB /* ASBasicImageDownloaderInternal.h in Headers */, 299DA1A91A828D2900162D41 /* ASBatchContext.h in Headers */, + 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */, 044285071BAA63FE00D16268 /* ASBatchFetching.h in Headers */, 055F1A3C19ABD43F004DAFF1 /* ASCellNode.h in Headers */, ACF6ED1C1B17843500DA7C62 /* ASCenterLayoutSpec.h in Headers */, @@ -1091,6 +1107,7 @@ ACF6ED221B17843500DA7C62 /* ASInsetLayoutSpec.h in Headers */, ACF6ED4B1B17847A00DA7C62 /* ASInternalHelpers.h in Headers */, ACF6ED241B17843500DA7C62 /* ASLayout.h in Headers */, + 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */, ACF6ED2A1B17843500DA7C62 /* ASLayoutable.h in Headers */, 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */, 464052241A3F83C40061C0BA /* ASLayoutController.h in Headers */, @@ -1125,6 +1142,7 @@ 9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */, ACF6ED311B17843500DA7C62 /* ASStaticLayoutSpec.h in Headers */, 055F1A3419ABD3E3004DAFF1 /* ASTableView.h in Headers */, + 251B8EF71BBB3D690087C538 /* ASCollectionDataController.h in Headers */, 0574D5E219C110940097DC25 /* ASTableViewProtocols.h in Headers */, 058D0A51195D05CB00B7D73C /* ASTextNode.h in Headers */, 058D0A5B195D05DC00B7D73C /* ASTextNodeCoreTextAdditions.h in Headers */, @@ -1474,6 +1492,7 @@ ACF6ED251B17843500DA7C62 /* ASLayout.mm in Sources */, 9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.mm in Sources */, 9C5FA35F1B90C9A500A62714 /* ASLayoutOptionsPrivate.mm in Sources */, + 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */, ACF6ED271B17843500DA7C62 /* ASLayoutSpec.mm in Sources */, 0516FA411A1563D200B4EBED /* ASMultiplexImageNode.mm in Sources */, 058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */, @@ -1490,6 +1509,7 @@ D785F6631A74327E00291744 /* ASScrollNode.m in Sources */, 058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */, 9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, + 251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */, ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */, ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, From 4c6585d3b32a96eba0d61a99f77c630a9a2dd921 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 15:10:50 -0700 Subject: [PATCH 12/68] Fix build errors from bad merge --- AsyncDisplayKit/ASCollectionView.mm | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2b1977bba8..18b39f9ee4 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -437,11 +437,6 @@ static BOOL _isInterceptedSelector(SEL sel) [_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone]; } -- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath -{ - return [_dataController nodeAtIndexPath:indexPath]; -} - - (NSString *)__reuseIdentifierForKind:(NSString *)kind { return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; @@ -700,7 +695,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { - return [self.layoutDelegate collectionView:self numerOfSectionsForSupplementaryKind:kind]; + return [self.layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { From a659ce7bf8945a140c68913dcd8ec2b07bb4c917 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 15:16:30 -0700 Subject: [PATCH 13/68] Cleanup setup of default flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 18b39f9ee4..1e4154852f 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -205,10 +205,6 @@ static BOOL _isInterceptedSelector(SEL sel) _dataController.delegate = _rangeController; _dataController.dataSource = self; - _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; - // TODO: Implement a better path of falling-back to a flow layout - _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; - _batchContext = [[ASBatchContext alloc] init]; _leadingScreensForBatching = 1.0; @@ -228,6 +224,15 @@ static BOOL _isInterceptedSelector(SEL sel) // and should not trigger a relayout. _ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero); + // Register the default layout inspector delegate for flow layouts, custom layouts + // will need to roll their own ASCollectionViewLayoutInspecting implementation. + if ([layout asdk_isFlowLayout]) { + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; + _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; + _flowLayoutInspector.collectionView = self; + _layoutDelegate = _flowLayoutInspector; + } + self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; From 73ddebed81b48d38b29f2f846556b35320b1b765 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 18:01:20 -0700 Subject: [PATCH 14/68] Implement UICollectionViewFlowLayout heuristics in a default inspector --- AsyncDisplayKit/ASCollectionView.mm | 35 ++++---- .../ASCollectionViewFlowLayoutInspector.h | 9 ++ .../ASCollectionViewFlowLayoutInspector.m | 83 ++++++++++++++++--- 3 files changed, 99 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 1e4154852f..a91be86ecd 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -688,21 +688,6 @@ static BOOL _isInterceptedSelector(SEL sel) return [_asyncDataSource collectionView:self numberOfItemsInSection:section]; } -- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; -} - -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section -{ - return [self.layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; -} - -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; -{ - return [self.layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; -} - - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController { if ([_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [_asyncDataSource numberOfSectionsInCollectionView:self]; @@ -731,8 +716,24 @@ static BOOL _isInterceptedSelector(SEL sel) } } -#pragma mark - -#pragma mark ASRangeControllerDelegate. +#pragma mark - ASCollectionViewDataControllerSource Supplementary view support + +- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; +} + +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +{ + return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; +} + +#pragma mark - ASRangeControllerDelegate. - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index e28ff0878f..962b6bfd18 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -14,10 +14,19 @@ @protocol ASCollectionViewLayoutInspecting +/** + * Asks the inspector + */ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +/** + * Asks the inspector for the number of supplementary sections in the collection view for the given kind. + */ - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; +/** + * Asks the inspector for the number of supplementary views for the given kind in the specified section. + */ - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 0a0b1598dc..d8cbd2fa7e 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -6,32 +6,93 @@ // // +#import + #import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionView.h" -@implementation ASCollectionViewFlowLayoutInspector +@implementation ASCollectionViewFlowLayoutInspector { + BOOL _delegateImplementsReferenceSizeForHeader; + BOOL _delegateImplementsReferenceSizeForFooter; +} + +#pragma mark - Accessors + +- (void)setLayout:(UICollectionViewFlowLayout *)layout +{ + _layout = layout; + _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; +} + +#pragma mark - ASCollectionViewLayoutInspecting - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - // TODO: Implement some heuristic that follows the width/height constraints of header and footer supplementary views - return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX)); + CGSize constrainedSize = CGSizeMake(FLT_MAX, FLT_MAX); + CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + constrainedSize.height = supplementarySize.height; + } else { + constrainedSize.width = supplementarySize.width; + } + return ASSizeRangeMake(CGSizeZero, constrainedSize); } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { - return self.collectionView.numberOfSections; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind +{ + return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; } - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - NSUInteger count = 0; - if (self.layout.headerReferenceSize.width > 0 || self.layout.headerReferenceSize.height > 0) { - count++; + return [self layoutHasSupplementaryViewOfKind:kind inSection:section] ? 1 : 0; +} + +#pragma mark - Private helpers + +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { + if (_delegateImplementsReferenceSizeForHeader) { + return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForHeaderInSection:section]; + } else { + return [self.layout headerReferenceSize]; + } + } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { + if (_delegateImplementsReferenceSizeForFooter) { + return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForFooterInSection:section]; + } else { + return [self.layout footerReferenceSize]; + } + } else { + return CGSizeZero; } - if (self.layout.footerReferenceSize.width > 0 || self.layout.footerReferenceSize.height > 0) { - count++; +} + +- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +{ + CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section]; + if ([self usedLayoutValueForSize:size] > 0) { + return YES; + } else { + return NO; } - return count; +} + +- (CGFloat)usedLayoutValueForSize:(CGSize)size +{ + if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { + return size.height; + } else { + return size.width; + } +} + +- (id)layoutDelegate +{ + return (id)self.collectionView.delegate; } @end From c9f93cde76d9a227a651c2150dc4fdba230e60a8 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 29 Sep 2015 18:06:00 -0700 Subject: [PATCH 15/68] Update copywright preamble --- .../Details/ASCollectionViewFlowLayoutInspector.h | 14 +++++++------- .../Details/ASCollectionViewFlowLayoutInspector.m | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 962b6bfd18..9adb624c96 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -1,10 +1,10 @@ -// -// ASCollectionViewFlowLayoutInspector.h -// Pods -// -// Created by Levi McCallum on 9/29/15. -// -// +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #import diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index d8cbd2fa7e..f3a825173c 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -1,10 +1,10 @@ -// -// ASCollectionViewFlowLayoutInspector.m -// Pods -// -// Created by Levi McCallum on 9/29/15. -// -// +/* Copyright (c) 2015-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 From bdd786f593ed4232e11ea5ed75d9be243a27c737 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 11:22:17 -0700 Subject: [PATCH 16/68] Document internal storage behaviors of ASDataController --- AsyncDisplayKit/Details/ASDataController.mm | 75 ++++++++++++++------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 844e61706b..0debc7cc4b 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -113,6 +113,33 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; + + // Processing in batches + for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) { + NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize)); + NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; + NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; + + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; + } +} + +/** + * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. + */ +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { + // Insert finished nodes into data storage + [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + }]; +} + - (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -154,32 +181,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; completionBlock(nodes, indexPaths); } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock -{ - NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; - - // Processing in batches - for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) { - NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize)); - NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; - NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; - } -} - -- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { - // Insert finished nodes into data storage - [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }]; -} - #pragma mark - Internal Data Querying + Editing +/** + * Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes. + * + * @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy + * of the editing nodes. The delegate is invoked on the main thread. + */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexPaths.count == 0) @@ -196,6 +205,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Removes the specified nodes at the given index paths and notifies the delegate of the nodes removed. + * + * @discussion Nodes are first removed from the editing store then removed from the completed store on the main thread. + * Once the backing stores are consistent, the delegate is invoked on the main thread. + */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexPaths.count == 0) @@ -211,6 +226,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Inserts sections, represented as arrays, into the backing store at the given indicies and notifies the delegate. + * + * @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted + * in the completed store on the main thread. The delegate is invoked on the main thread. + */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexSet.count == 0) @@ -227,6 +248,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }); } +/** + * Removes sections at the given indicies from the backing store and notifies the delegate. + * + * @discussion Section array are first removed from the editing store, then the associated section in the completed + * store is removed on the main thread. The delegate is invoked on the main thread. + */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { if (indexSet.count == 0) From a3dce24fdccaf9783de5193572023ebaa3678322 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 12:10:48 -0700 Subject: [PATCH 17/68] Document data controller node population --- AsyncDisplayKit/Details/ASDataController.mm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 0debc7cc4b..6b91f4beb1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -354,6 +354,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +/** + * Fetches row nodes and their specified index paths for the provided sections from the data source. + * + * @discussion Results are stored in the passed mutable arrays. + */ - (void)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { @@ -368,6 +373,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +/** + * Fetches row nodes and their specified index paths for all sections from the data source. + * + * @discussion Results are stored in the passed mutable arrays. + */ - (void)_populateFromEntireDataSourceWithMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; From 835f9e99cac65737f19d72e4ad5e840edf1ba582 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 30 Sep 2015 13:25:39 -0700 Subject: [PATCH 18/68] Batch render supplementary views on reload data --- AsyncDisplayKit/ASCollectionView.mm | 20 +++++++--- .../Details/ASCollectionDataController.mm | 39 ++++++++++--------- .../Details/ASDataController+Subclasses.h | 10 +++++ AsyncDisplayKit/Details/ASDataController.mm | 15 +++++++ 4 files changed, 61 insertions(+), 23 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index a91be86ecd..2747d11e11 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -153,6 +153,8 @@ static BOOL _isInterceptedSelector(SEL sel) CGSize _maxSizeForNodesConstrainedSize; BOOL _ignoreMaxSizeChange; + NSMutableArray *_registeredSupplementaryKinds; + /** * If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it. @@ -233,6 +235,8 @@ static BOOL _isInterceptedSelector(SEL sel) _layoutDelegate = _flowLayoutInspector; } + _registeredSupplementaryKinds = [NSMutableArray array]; + self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:@"_ASCollectionViewCell"]; @@ -390,6 +394,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { + [_registeredSupplementaryKinds addObject:elementKind]; [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; } @@ -637,11 +642,6 @@ static BOOL _isInterceptedSelector(SEL sel) return node; } -- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; -} - - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { ASSizeRange constrainedSize; @@ -718,6 +718,16 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - ASCollectionViewDataControllerSource Supplementary view support +- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; +} + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController +{ + return _registeredSupplementaryKinds; +} + - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 1fa63a4999..4d305a358a 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -23,29 +23,32 @@ @implementation ASCollectionDataController { NSMutableDictionary *_completedSupplementaryNodes; NSMutableDictionary *_editingSupplementaryNodes; + + NSMutableDictionary *_pendingNodes; + NSMutableDictionary *_pendingIndexPaths; } -- (void)initialSupplementaryLoading +- (void)prepareForReloadData { - [self performEditCommandWithBlock:^{ - ASDisplayNodeAssertMainThread(); - [self accessDataSourceWithBlock:^{ - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { - _completedSupplementaryNodes[kind] = [NSMutableArray array]; - _editingSupplementaryNodes[kind] = [NSMutableArray array]; - - NSMutableArray *indexPaths = [NSMutableArray array]; - NSMutableArray *nodes = [NSMutableArray array]; - [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:nil]; - }]; - }]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + NSMutableArray *indexPaths = [NSMutableArray array]; + NSMutableArray *nodes = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; }]; } - + +- (void)willReloadData +{ + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } completion:nil]; + }]; +} + - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index e07bb5d031..21a6a28f57 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -30,4 +30,14 @@ */ - (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * An opportunity for a subclass to access the data source before entering into the editing queue + */ +- (void)prepareForReloadData; + +/** + * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. + */ +- (void)willReloadData; + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 6b91f4beb1..d47cc072f1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -308,6 +308,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Measure nodes whose views are loaded before we leave the main thread [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + + // Allow subclasses to perform setup before going into the edit transaction + [self prepareForReloadData]; [_editingTransactionQueue addOperationWithBlock:^{ ASLOG(@"Edit Transaction - reloadData"); @@ -319,6 +322,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [self willReloadData]; + // Insert each section NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; for (int i = 0; i < sectionCount; i++) { @@ -337,6 +342,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)prepareForReloadData +{ + // Implemented by subclasses +} + +- (void)willReloadData +{ + // Implemented by subclasses +} + #pragma mark - Data Source Access (Calling _dataSource) - (void)accessDataSourceWithBlock:(dispatch_block_t)block From afda471bd0ba1ff836c4b62fe7dbb78e2fa1c670 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 15:03:09 -0700 Subject: [PATCH 19/68] Clean up interfacing with internal editing/completed stores --- .../Details/ASCollectionDataController.mm | 57 ++---- .../ASCollectionViewFlowLayoutInspector.m | 6 +- .../Details/ASDataController+Subclasses.h | 20 +- AsyncDisplayKit/Details/ASDataController.mm | 176 ++++++++++++------ AsyncDisplayKit/Details/ASRangeController.mm | 6 +- .../ASCollectionView/Sample/ViewController.m | 1 + 6 files changed, 161 insertions(+), 105 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 4d305a358a..2e19c3974e 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -8,6 +8,7 @@ #import "ASCollectionDataController.h" +#import "ASLog.h" #import "ASAssert.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNode.h" @@ -21,17 +22,18 @@ @end @implementation ASCollectionDataController { - NSMutableDictionary *_completedSupplementaryNodes; - NSMutableDictionary *_editingSupplementaryNodes; - NSMutableDictionary *_pendingNodes; NSMutableDictionary *_pendingIndexPaths; } - (void)prepareForReloadData { + _pendingNodes = [NSMutableDictionary dictionary]; + _pendingIndexPaths = [NSMutableDictionary dictionary]; + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + ASLOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -43,9 +45,21 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + ASLOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + + // Insert each section + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; + for (int i = 0; i < sectionCount; i++) { + [sections addObject:[[NSMutableArray alloc] init]]; + } + [self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil]; + [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:nil]; + } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; }]; } @@ -66,7 +80,7 @@ - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - return _completedSupplementaryNodes[kind][indexPath.section][indexPath.item]; + return [self internalCompletedNodes][kind][indexPath.section][indexPath.item]; } - (id)collectionDataSource @@ -74,37 +88,4 @@ return (id)self.dataSource; } -#pragma mark - Internal Data Querying - -// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController -- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths -{ - if (indexPaths.count == 0) - return; - NSMutableArray *editingNodes = [self _editingNodesOfKind:kind]; - ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); - NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); - ASDisplayNodePerformBlockOnMainThread(^{ - _completedSupplementaryNodes[kind] = completedNodes; - // TODO: Notify change - }); -} - -// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController -- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths -{ - if (indexPaths.count == 0) - return; -} - -- (NSMutableArray *)_completedNodesOfKind:(NSString *)kind -{ - return _completedSupplementaryNodes[kind]; -} - -- (NSMutableArray *)_editingNodesOfKind:(NSString *)kind -{ - return _editingSupplementaryNodes[kind]; -} - @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index f3a825173c..41b39c144a 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -42,7 +42,11 @@ - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind { - return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; + if ([collectionView.asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { + return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; + } else { + return 1; + } } - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 21a6a28f57..84f07bf34d 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,11 +25,6 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; -/** - * Measure and layout the given nodes in optimized batches, constraining each to a given size. - */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; - /** * An opportunity for a subclass to access the data source before entering into the editing queue */ @@ -40,4 +35,19 @@ */ - (void)willReloadData; +- (NSMutableDictionary *)internalCompletedNodes; + +/** + * Measure and layout the given nodes in optimized batches, constraining each to a given size. + */ +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; + +- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock; + +- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; + @end diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index d47cc072f1..479ba381dd 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -19,12 +19,16 @@ const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; +NSString * const ASRowNodeKind = @"_ASRowNodeKind"; + static void *kASSizingQueueContext = &kASSizingQueueContext; @interface ASDataController () { NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available. - NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable. - NSMutableArray *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes. + NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable. + NSMutableDictionary *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes. + + NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking. NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes. @@ -50,9 +54,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; return nil; } - _completedNodes = [NSMutableArray array]; - _editingNodes = [NSMutableArray array]; + _completedNodes = [NSMutableDictionary dictionary]; + _editingNodes = [NSMutableDictionary dictionary]; + _completedNodes[ASRowNodeKind] = [NSMutableArray array]; + _editingNodes[ASRowNodeKind] = [NSMutableArray array]; + _pendingEditCommandBlocks = [NSMutableArray array]; _editingTransactionQueue = [[NSOperationQueue alloc] init]; @@ -178,7 +185,78 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER); free(nodeBoundSizes); - completionBlock(nodes, indexPaths); + if (completionBlock) + completionBlock(nodes, indexPaths); +} + +#pragma mark - External Data Querying + Editing + +- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + if (indexPaths.count == 0) + return; + + NSMutableArray *editingNodes = _editingNodes[kind]; + ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); + _editingNodes[kind] = editingNodes; + + // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. + NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes); + + ASDisplayNodePerformBlockOnMainThread(^{ + _completedNodes[kind] = completedNodes; + if (completionBlock) { + completionBlock(nodes, indexPaths); + } + }); +} + +- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +{ + if (indexPaths.count == 0) + return; + ASLOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); + NSMutableArray *editingNodes = _editingNodes[kind]; + ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); + _editingNodes[kind] = editingNodes; + + ASDisplayNodePerformBlockOnMainThread(^{ + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); + ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths); + if (completionBlock) { + completionBlock(nodes, indexPaths); + } + }); +} + +- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock +{ + if (indexSet.count == 0) + return; + [_editingNodes[kind] insertObjects:sections atIndexes:indexSet]; + + // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. + NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections); + + ASDisplayNodePerformBlockOnMainThread(^{ + [_completedNodes[kind] insertObjects:sectionsForCompleted atIndexes:indexSet]; + if (completionBlock) { + completionBlock(sections, indexSet); + } + }); +} + +- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock +{ + if (indexSet.count == 0) + return; + [_editingNodes[kind] removeObjectsAtIndexes:indexSet]; + ASDisplayNodePerformBlockOnMainThread(^{ + [_completedNodes[kind] removeObjectsAtIndexes:indexSet]; + if (completionBlock) { + completionBlock(indexSet); + } + }); } #pragma mark - Internal Data Querying + Editing @@ -191,18 +269,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexPaths.count == 0) - return; - ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths, nodes); - - // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. - NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_editingNodes); - - ASDisplayNodePerformBlockOnMainThread(^{ - _completedNodes = completedNodes; + [self insertNodes:nodes ofKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidInsertNodes) [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -213,17 +283,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexPaths.count == 0) - return; - ASLOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); - ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); - - ASDisplayNodePerformBlockOnMainThread(^{ - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); - ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths); + [self deleteNodesOfKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidDeleteNodes) [_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -234,18 +297,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexSet.count == 0) - return; - [_editingNodes insertObjects:sections atIndexes:indexSet]; - - // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. - NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections); - - ASDisplayNodePerformBlockOnMainThread(^{ - [_completedNodes insertObjects:sectionsForCompleted atIndexes:indexSet]; + [self insertSections:sections ofKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { if (_delegateDidInsertSections) [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; - }); + }]; } /** @@ -256,14 +311,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - if (indexSet.count == 0) - return; - [_editingNodes removeObjectsAtIndexes:indexSet]; - ASDisplayNodePerformBlockOnMainThread(^{ - [_completedNodes removeObjectsAtIndexes:indexSet]; + [self deleteSectionsOfKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { if (_delegateDidDeleteSections) [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; - }); + }]; } #pragma mark - Initial Load & Full Reload (External API) @@ -316,7 +367,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASLOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; @@ -435,7 +486,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodePerformBlockOnMainThread(^{ // Deep copy _completedNodes to _externalCompletedNodes. // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. - _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes); + _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]); ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; @@ -515,7 +566,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -544,9 +595,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); - ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes)); + ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -570,8 +621,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASLOG(@"Edit Transaction - moveSection"); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; // update the section of indexpaths @@ -696,7 +747,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - relayoutNodesBlock(_completedNodes); + relayoutNodesBlock(_completedNodes[ASRowNodeKind]); }); }]; }]; @@ -711,7 +762,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -722,6 +773,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Data Querying (Subclass API) + +- (NSMutableDictionary *)internalCompletedNodes +{ + return _completedNodes; +} + #pragma mark - Data Querying (External API) - (NSUInteger)numberOfSections @@ -771,7 +829,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)completedNodes { ASDisplayNodeAssertMainThread(); - return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes; + return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASRowNodeKind]; } #pragma mark - Dealloc @@ -779,15 +837,17 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)dealloc { ASDisplayNodeAssertMainThread(); - [_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - if (node.isNodeLoaded) { - if (node.layerBacked) { - [node.layer removeFromSuperlayer]; - } else { - [node.view removeFromSuperview]; + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL * _Nonnull stop) { + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { + if (node.isNodeLoaded) { + if (node.layerBacked) { + [node.layer removeFromSuperlayer]; + } else { + [node.view removeFromSuperview]; + } } - } + }]; }]; }]; } diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 985e6f60cc..663f9bea1c 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -158,9 +158,9 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)cellNode +- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node { - if (cellNode.view.superview == contentView) { + if (node.view.superview == contentView) { // this content view is already correctly configured return; } @@ -170,7 +170,7 @@ [view removeFromSuperview]; } - [self moveNode:cellNode toView:contentView]; + [self moveNode:node toView:contentView]; } diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 885ef3e00f..6f0ca8e5aa 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -33,6 +33,7 @@ UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.headerReferenceSize = CGSizeMake(50.0, 50.0); _collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES]; _collectionView.asyncDataSource = self; From f041a273b59128cc09b920142bf84255a10e8938 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 18:51:14 -0700 Subject: [PATCH 20/68] Use rows mutable array for section deletion --- AsyncDisplayKit/Details/ASDataController.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 479ba381dd..ed67bc6f19 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -370,7 +370,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)]; + NSMutableArray *editingNodes = _editingNodes[ASRowNodeKind]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; [self willReloadData]; From 6e49e1f4d539601e9bb625c7fa161b54f3c23eb9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 19:18:25 -0700 Subject: [PATCH 21/68] Initialize mutable editing nodes before section insertion --- AsyncDisplayKit/Details/ASDataController.mm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index ed67bc6f19..b1b0e9ce93 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -195,7 +195,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - + NSMutableArray *editingNodes = _editingNodes[kind]; ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes); _editingNodes[kind] = editingNodes; @@ -233,6 +233,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexSet.count == 0) return; + + if (_editingNodes[kind] == nil) { + _editingNodes[kind] = [NSMutableArray array]; + } + [_editingNodes[kind] insertObjects:sections atIndexes:indexSet]; // Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads. From e410595f428a470a5abdc6528a4c91fd48cac18e Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:05:07 -0700 Subject: [PATCH 22/68] Add functioning header/footer supplementary view support to example --- .../Sample.xcodeproj/project.pbxproj | 6 +++ .../Sample/SupplementaryNode.h | 18 +++++++ .../Sample/SupplementaryNode.m | 54 +++++++++++++++++++ .../ASCollectionView/Sample/ViewController.m | 6 ++- 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 examples/ASCollectionView/Sample/SupplementaryNode.h create mode 100644 examples/ASCollectionView/Sample/SupplementaryNode.m diff --git a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj index 03c3323afb..959990a70e 100644 --- a/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/ASCollectionView/Sample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */; settings = {ASSET_TAGS = (); }; }; 9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; settings = {ASSET_TAGS = (); }; }; AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; }; AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; }; @@ -17,6 +18,8 @@ /* Begin PBXFileReference section */ 2DBAEE96397BB913350C4530 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; + 9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SupplementaryNode.h; sourceTree = ""; }; + 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SupplementaryNode.m; sourceTree = ""; }; 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = ""; }; AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -78,6 +81,8 @@ AC3C4A691A11F47200143C57 /* ViewController.m */, AC3C4A8D1A11F80C00143C57 /* Images.xcassets */, AC3C4A611A11F47200143C57 /* Supporting Files */, + 9B92C87F1BC17D3000EE46B2 /* SupplementaryNode.h */, + 9B92C8801BC17D3000EE46B2 /* SupplementaryNode.m */, ); indentWidth = 2; path = Sample; @@ -208,6 +213,7 @@ buildActionMask = 2147483647; files = ( AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */, + 9B92C8811BC17D3000EE46B2 /* SupplementaryNode.m in Sources */, AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */, AC3C4A641A11F47200143C57 /* main.m in Sources */, ); diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.h b/examples/ASCollectionView/Sample/SupplementaryNode.h new file mode 100644 index 0000000000..e64ac9e3f6 --- /dev/null +++ b/examples/ASCollectionView/Sample/SupplementaryNode.h @@ -0,0 +1,18 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import + +@interface SupplementaryNode : ASDisplayNode + +- (instancetype)initWithText:(NSString *)text; + +@end diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.m b/examples/ASCollectionView/Sample/SupplementaryNode.m new file mode 100644 index 0000000000..baddef62c5 --- /dev/null +++ b/examples/ASCollectionView/Sample/SupplementaryNode.m @@ -0,0 +1,54 @@ +/* This file provided by Facebook is for non-commercial testing and evaluation + * purposes only. Facebook reserves all rights not expressly granted. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import "SupplementaryNode.h" + +#import +#import +#import + +static CGFloat kInsets = 15.0; + +@implementation SupplementaryNode { + ASTextNode *_textNode; +} + +- (instancetype)initWithText:(NSString *)text +{ + self = [super init]; + if (self != nil) { + _textNode = [[ASTextNode alloc] init]; + _textNode.attributedString = [[NSAttributedString alloc] initWithString:text + attributes:[self textAttributes]]; + [self addSubnode:_textNode]; + } + return self; +} + +- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize +{ + ASCenterLayoutSpec *center = [[ASCenterLayoutSpec alloc] init]; + center.child = _textNode; + UIEdgeInsets insets = UIEdgeInsetsMake(kInsets, kInsets, kInsets, kInsets); + return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:center]; +} + +#pragma mark - Text Formatting + +- (NSDictionary *)textAttributes +{ + return @{ + NSFontAttributeName: [UIFont systemFontOfSize:18.0], + NSForegroundColorAttributeName: [UIColor whiteColor], + }; +} + +@end diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 6f0ca8e5aa..9587bea933 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -12,6 +12,7 @@ #import "ViewController.h" #import +#import "SupplementaryNode.h" @interface ViewController () { @@ -32,8 +33,8 @@ return nil; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; - layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; layout.headerReferenceSize = CGSizeMake(50.0, 50.0); + layout.footerReferenceSize = CGSizeMake(50.0, 50.0); _collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES]; _collectionView.asyncDataSource = self; @@ -79,7 +80,8 @@ - (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNode *node = [[ASDisplayNode alloc] init]; + NSString *text = [kind isEqualToString:UICollectionElementKindSectionHeader] ? @"Header" : @"Footer"; + SupplementaryNode *node = [[SupplementaryNode alloc] initWithText:text]; if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { node.backgroundColor = [UIColor blueColor]; } else { From aa1842e4848f8262cff01d4c6939c442c5e640b9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:16:07 -0700 Subject: [PATCH 23/68] Remove non-null definitions --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 2e19c3974e..4de5bd3ca6 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -32,7 +32,7 @@ _pendingIndexPaths = [NSMutableDictionary dictionary]; NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) { + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { ASLOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index b1b0e9ce93..b55b62e528 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -843,7 +843,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (void)dealloc { ASDisplayNodeAssertMainThread(); - [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL * _Nonnull stop) { + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { if (node.isNodeLoaded) { From e4f274aebb8cf1f2f10e0e8f35f741033ee81768 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 4 Oct 2015 18:37:28 -0700 Subject: [PATCH 24/68] Expose completed nodes array by kind --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 4de5bd3ca6..03b981a2a3 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -80,7 +80,7 @@ - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); - return [self internalCompletedNodes][kind][indexPath.section][indexPath.item]; + return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; } - (id)collectionDataSource diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 84f07bf34d..fda6e69cca 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -35,7 +35,7 @@ */ - (void)willReloadData; -- (NSMutableDictionary *)internalCompletedNodes; +- (NSArray *)completedNodesOfKind:(NSString *)kind; /** * Measure and layout the given nodes in optimized batches, constraining each to a given size. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index b55b62e528..94592578c8 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -781,9 +781,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Querying (Subclass API) -- (NSMutableDictionary *)internalCompletedNodes +- (NSArray *)completedNodesOfKind:(NSString *)kind { - return _completedNodes; + return _completedNodes[kind]; } #pragma mark - Data Querying (External API) From 1c2046cdf4be9deea2af1d63866856185125f4bf Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:20:50 -0700 Subject: [PATCH 25/68] Localize debugging LOG definition --- AsyncDisplayKit/ASTableView.mm | 16 +++--- .../Details/ASCollectionDataController.mm | 8 +-- AsyncDisplayKit/Details/ASDataController.mm | 52 ++++++++++--------- Base/ASLog.h | 3 -- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index e5122e71b5..a8dc8c6f9d 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -8,7 +8,6 @@ #import "ASTableView.h" -#import "ASLog.h" #import "ASAssert.h" #import "ASDataController.h" #import "ASCollectionViewLayoutController.h" @@ -22,6 +21,9 @@ // FIXME: Temporary nonsense import until method names are finalized and exposed #import "ASDisplayNode+Subclasses.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + #pragma mark - #pragma mark Proxying. @@ -647,7 +649,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); - ASLOG(@"--- UITableView beginUpdates"); + LOG(@"--- UITableView beginUpdates"); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -663,7 +665,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion { ASDisplayNodeAssertMainThread(); - ASLOG(@"--- UITableView endUpdates"); + LOG(@"--- UITableView endUpdates"); if (!self.asyncDataSource) { if (completion) { @@ -749,7 +751,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView insertRows:%ld rows", indexPaths.count); + LOG(@"UITableView insertRows:%ld rows", indexPaths.count); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -768,7 +770,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView deleteRows:%ld rows", indexPaths.count); + LOG(@"UITableView deleteRows:%ld rows", indexPaths.count); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes @@ -787,7 +789,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView insertSections:%@", indexSet); + LOG(@"UITableView insertSections:%@", indexSet); if (!self.asyncDataSource) { @@ -803,7 +805,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { ASDisplayNodeAssertMainThread(); - ASLOG(@"UITableView deleteSections:%@", indexSet); + LOG(@"UITableView deleteSections:%@", indexSet); if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 03b981a2a3..d8dd39ec18 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -8,13 +8,15 @@ #import "ASCollectionDataController.h" -#import "ASLog.h" #import "ASAssert.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNode.h" #import "ASDisplayNodeInternal.h" #import "ASDataController+Subclasses.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + @interface ASCollectionDataController () - (id)collectionDataSource; @@ -33,7 +35,7 @@ NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { - ASLOG(@"Populating elements of kind: %@", kind); + LOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -45,7 +47,7 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { - ASLOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + LOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); // Insert each section NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 94592578c8..4bc6328c42 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -10,13 +10,15 @@ #import -#import "ASLog.h" #import "ASAssert.h" #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" #import "ASDisplayNodeInternal.h" +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; NSString * const ASRowNodeKind = @"_ASRowNodeKind"; @@ -215,7 +217,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { if (indexPaths.count == 0) return; - ASLOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); + LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); NSMutableArray *editingNodes = _editingNodes[kind]; ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); _editingNodes[kind] = editingNodes; @@ -369,7 +371,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self prepareForReloadData]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - reloadData"); + LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); @@ -486,7 +488,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _batchUpdateCounter--; if (_batchUpdateCounter == 0) { - ASLOG(@"endUpdatesWithCompletion - beginning"); + LOG(@"endUpdatesWithCompletion - beginning"); [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ @@ -494,16 +496,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]); - ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate"); + LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; }); }]; // Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates. // Each subsequent command in the queue will also wait on the full asynchronous completion of the prior command's edit transaction. - ASLOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); + LOG(@"endUpdatesWithCompletion - %zd blocks to run", _pendingEditCommandBlocks.count); [_pendingEditCommandBlocks enumerateObjectsUsingBlock:^(dispatch_block_t block, NSUInteger idx, BOOL *stop) { - ASLOG(@"endUpdatesWithCompletion - running block #%zd", idx); + LOG(@"endUpdatesWithCompletion - running block #%zd", idx); block(); }]; [_pendingEditCommandBlocks removeAllObjects]; @@ -513,7 +515,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Now that the transaction is done, _completedNodes can be accessed externally again. _externalCompletedNodes = nil; - ASLOG(@"endUpdatesWithCompletion - calling delegate end"); + LOG(@"endUpdatesWithCompletion - calling delegate end"); [_delegate dataController:self endUpdatesAnimated:animated completion:completion]; }); }]; @@ -537,7 +539,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - insertSections: %@", indexSet); + LOG(@"Edit Command - insertSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -549,7 +551,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - insertSections: %@", indexSet); + LOG(@"Edit Transaction - insertSections: %@", indexSet); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; for (NSUInteger i = 0; i < indexSet.count; i++) { [sectionArray addObject:[NSMutableArray array]]; @@ -566,12 +568,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - deleteSections: %@", indexSet); + LOG(@"Edit Command - deleteSections: %@", indexSet); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - ASLOG(@"Edit Transaction - deleteSections: %@", indexSet); + LOG(@"Edit Transaction - deleteSections: %@", indexSet); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -584,7 +586,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - reloadSections: %@", sections); + LOG(@"Edit Command - reloadSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -603,7 +605,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); - ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); + LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -618,14 +620,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - moveSection"); + LOG(@"Edit Command - moveSection"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements - ASLOG(@"Edit Transaction - moveSection"); + LOG(@"Edit Transaction - moveSection"); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); @@ -650,7 +652,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - insertRows: %@", indexPaths); + LOG(@"Edit Command - insertRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -666,7 +668,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - insertRows: %@", indexPaths); + LOG(@"Edit Transaction - insertRows: %@", indexPaths); [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -677,7 +679,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - deleteRows: %@", indexPaths); + LOG(@"Edit Command - deleteRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -686,7 +688,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - deleteRows: %@", indexPaths); + LOG(@"Edit Transaction - deleteRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions]; }]; }]; @@ -696,7 +698,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - reloadRows: %@", indexPaths); + LOG(@"Edit Command - reloadRows: %@", indexPaths); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; @@ -716,7 +718,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - reloadRows: %@", indexPaths); + LOG(@"Edit Transaction - reloadRows: %@", indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -728,7 +730,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - relayoutRows"); + LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { @@ -763,11 +765,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - ASLOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); + LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ - ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); + LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; diff --git a/Base/ASLog.h b/Base/ASLog.h index c2f3de15c4..4c85bdf99a 100644 --- a/Base/ASLog.h +++ b/Base/ASLog.h @@ -8,9 +8,6 @@ #pragma once -//#define ASLOG(...) NSLog(__VA_ARGS__) -#define ASLOG(...) - #define ASMultiplexImageNodeLogDebug(...) #define ASMultiplexImageNodeCLogDebug(...) From 0086949a65cbec108be9d6ad6064165bcd9ede44 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:23:02 -0700 Subject: [PATCH 26/68] Fix typo --- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index fda6e69cca..6a65036fbc 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -40,7 +40,7 @@ /** * Measure and layout the given nodes in optimized batches, constraining each to a given size. */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 4bc6328c42..e1dff93f81 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -122,7 +122,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -132,7 +132,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock]; + [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constrainedSizeBlock completion:completionBlock]; } } @@ -149,7 +149,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -165,7 +165,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = constraintedSizeBlock(indexPaths[k]); + nodeBoundSizes[k] = constrainedSizeBlock(indexPaths[k]); } } From ecbc29fecab7ebac8e1318a78d367b9c56c0b526 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:31:54 -0700 Subject: [PATCH 27/68] Fix travis build --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 7c0956d4d3..6e105257a3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -211,6 +211,8 @@ 509E68651B3AEDC5009B9150 /* CGRect+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; }; 6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; }; + 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; }; 9C49C36F1B853957000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1568,6 +1570,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */, + 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, From b436a0100021cf152ea93878bd64c924e31c4a4d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:35:29 -0700 Subject: [PATCH 28/68] Assert supplementary node kind registration --- AsyncDisplayKit/ASCollectionView.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2747d11e11..4ee5e2f63f 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -394,6 +394,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)registerSupplementaryNodeOfKind:(NSString *)elementKind { + ASDisplayNodeAssert(elementKind != nil, @"A kind is needed for supplementary node registration"); [_registeredSupplementaryKinds addObject:elementKind]; [self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]]; From ba4298e6bcf08d2a7d269afd54356c9dc0c4d21b Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 10:36:47 -0700 Subject: [PATCH 29/68] Optimize string concatenation --- AsyncDisplayKit/ASCollectionView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 4ee5e2f63f..b2f40d35e6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -450,7 +450,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (NSString *)__reuseIdentifierForKind:(NSString *)kind { - return [NSString stringWithFormat:@"_ASCollectionSupplementaryView_%@", kind]; + return [@"_ASCollectionSupplementaryView_" stringByAppendingString:kind]; } #pragma mark - From 34f3065825c0c16f18d2948901b3ac2d2841de83 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 5 Oct 2015 13:56:23 -0700 Subject: [PATCH 30/68] Use local method for introspecting data controller constrained sizes --- .../Details/ASCollectionDataController.mm | 13 ++++- .../Details/ASDataController+Subclasses.h | 24 +++++++- AsyncDisplayKit/Details/ASDataController.h | 2 + AsyncDisplayKit/Details/ASDataController.mm | 55 ++++++++++--------- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index d8dd39ec18..69f041a62d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -57,14 +57,21 @@ } [self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil]; - [self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; }]; } +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } else { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } +} + - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 6a65036fbc..03e1e32089 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -35,19 +35,39 @@ */ - (void)willReloadData; +/** + * Read only access to the underlying completed nodes of the given kind + */ - (NSArray *)completedNodesOfKind:(NSString *)kind; /** - * Measure and layout the given nodes in optimized batches, constraining each to a given size. + * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. */ -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Provides the size range for a specific node during the layout process. + */ +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Inserts the given nodes of the specified kind into the backing store, calling completion on the main thread when the write finishes. + */ - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Deletes the given nodes of the specified kind in the backing store, calling completion on the main thread when the deletion finishes. + */ - (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/** + * Inserts the given sections of the specified kind in the backing store, calling completion on the main thread when finished. + */ - (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock; +/** + * Deletes the given sections of the specified kind in the backing store, calling completion on the main thread when finished. + */ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; @end diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 39d0b78792..ea4e4694a3 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -14,6 +14,8 @@ @class ASCellNode; @class ASDataController; +FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind; + typedef NSUInteger ASDataControllerAnimationOptions; /** diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index e1dff93f81..0eab204e35 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -21,7 +21,7 @@ const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; -NSString * const ASRowNodeKind = @"_ASRowNodeKind"; +NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; static void *kASSizingQueueContext = &kASSizingQueueContext; @@ -59,8 +59,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _completedNodes = [NSMutableDictionary dictionary]; _editingNodes = [NSMutableDictionary dictionary]; - _completedNodes[ASRowNodeKind] = [NSMutableArray array]; - _editingNodes[ASRowNodeKind] = [NSMutableArray array]; + _completedNodes[ASDataControllerRowNodeKind] = [NSMutableArray array]; + _editingNodes[ASDataControllerRowNodeKind] = [NSMutableArray array]; _pendingEditCommandBlocks = [NSMutableArray array]; @@ -115,14 +115,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { ASCellNode *node = nodes[idx]; if (node.isNodeLoaded) { - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; [node measureWithSizeRange:constrainedSize]; node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); } }]; } -- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -132,7 +132,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constrainedSizeBlock completion:completionBlock]; + [self _layoutNodes:batchedNodes ofKind:kind atIndexPaths:batchedIndexPaths completion:completionBlock]; } } @@ -141,15 +141,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) { - return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - } completion:^(NSArray *nodes, NSArray *indexPaths) { + [self batchLayoutNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { // Insert finished nodes into data storage [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; } -- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constrainedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock +- (void)_layoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue"); @@ -165,7 +163,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; if (!node.isNodeLoaded) { - nodeBoundSizes[k] = constrainedSizeBlock(indexPaths[k]); + nodeBoundSizes[k] = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPaths[k]]; } } @@ -191,6 +189,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; completionBlock(nodes, indexPaths); } +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; +} + #pragma mark - External Data Querying + Editing - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock @@ -276,7 +279,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertNodes:nodes ofKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidInsertNodes) [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -290,7 +293,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self deleteNodesOfKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { + [self deleteNodesOfKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { if (_delegateDidDeleteNodes) [_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; }]; @@ -304,7 +307,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertSections:sections ofKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { + [self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { if (_delegateDidInsertSections) [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; }]; @@ -318,7 +321,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self deleteSectionsOfKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { + [self deleteSectionsOfKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) { if (_delegateDidDeleteSections) [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; }]; @@ -374,10 +377,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Transaction - reloadData"); // Remove everything that existed before the reload, now that we're ready to insert replacements - NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - NSMutableArray *editingNodes = _editingNodes[ASRowNodeKind]; + NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -494,7 +497,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodePerformBlockOnMainThread(^{ // Deep copy _completedNodes to _externalCompletedNodes. // Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate. - _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]); + _externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASDataControllerRowNodeKind]); LOG(@"endUpdatesWithCompletion - begin updates call to delegate"); [_delegate dataControllerBeginUpdates:self]; @@ -574,7 +577,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ // remove elements LOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], indexSet); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; @@ -603,9 +606,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); - LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind])); + LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -629,8 +632,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Transaction - moveSection"); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], indexPaths); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; // update the section of indexpaths @@ -755,7 +758,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - relayoutNodesBlock(_completedNodes[ASRowNodeKind]); + relayoutNodesBlock(_completedNodes[ASDataControllerRowNodeKind]); }); }]; }]; @@ -770,7 +773,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath); - NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASDataControllerRowNodeKind], [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; @@ -837,7 +840,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; - (NSArray *)completedNodes { ASDisplayNodeAssertMainThread(); - return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASRowNodeKind]; + return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASDataControllerRowNodeKind]; } #pragma mark - Dealloc From 8efca60bac6167e56eadb5aea719a09d3ba7c37d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:26:23 -0700 Subject: [PATCH 31/68] Handle initial sizing delegate implementation in flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 26 ++++++++++++++----- .../ASCollectionViewFlowLayoutInspector.h | 4 ++- .../ASCollectionViewFlowLayoutInspector.m | 26 ++++++++++++++----- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b2f40d35e6..3dff9423af 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -228,13 +228,8 @@ static BOOL _isInterceptedSelector(SEL sel) // Register the default layout inspector delegate for flow layouts, custom layouts // will need to roll their own ASCollectionViewLayoutInspecting implementation. - if ([layout asdk_isFlowLayout]) { - _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init]; - _flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout; - _flowLayoutInspector.collectionView = self; - _layoutDelegate = _flowLayoutInspector; - } - + _layoutDelegate = [self flowLayoutInspector]; + _registeredSupplementaryKinds = [NSMutableArray array]; self.backgroundColor = [UIColor whiteColor]; @@ -252,6 +247,21 @@ static BOOL _isInterceptedSelector(SEL sel) super.dataSource = nil; } +/** + * A layout inspector implementation specific for the sizing behavior of UICollectionViewFlowLayouts + * + * @discussion Will return nil if the current layout is not a flow layout + */ +- (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector +{ + if (_flowLayoutInspector == nil) { + UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout; + _flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self + flowLayout:layout]; + } + return _flowLayoutInspector; +} + #pragma mark - #pragma mark Overrides. @@ -326,6 +336,8 @@ static BOOL _isInterceptedSelector(SEL sel) super.delegate = (id)_proxyDelegate; _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } + + _flowLayoutInspector.collectionView = self; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 9adb624c96..94d65e2117 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -33,8 +33,10 @@ @interface ASCollectionViewFlowLayoutInspector : NSObject -@property (nonatomic, weak) ASCollectionView *collectionView; @property (nonatomic, weak) UICollectionViewFlowLayout *layout; +@property (nonatomic, weak) ASCollectionView *collectionView; + +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 41b39c144a..4c486d5d00 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -19,11 +19,25 @@ #pragma mark - Accessors -- (void)setLayout:(UICollectionViewFlowLayout *)layout +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout { - _layout = layout; - _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; - _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + if (flowLayout == nil) { + return nil; + } + + self = [super init]; + if (self != nil) { + self.collectionView = collectionView; + _layout = flowLayout; + } + return self; +} + +- (void)setCollectionView:(ASCollectionView *)collectionView +{ + _collectionView = collectionView; + _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; } #pragma mark - ASCollectionViewLayoutInspecting @@ -94,9 +108,9 @@ } } -- (id)layoutDelegate +- (id)layoutDelegate { - return (id)self.collectionView.delegate; + return (id)_collectionView.asyncDelegate; } @end From 2958c848aa747ed467a4cb755c3b9d5ff5073cc5 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:27:17 -0700 Subject: [PATCH 32/68] Asset presence of async delegate before accessing layout delegate --- AsyncDisplayKit/ASCollectionView.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 3dff9423af..8fba37b7b8 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -743,16 +743,19 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { + ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } From 769a67569c3a965fde662f4122d0c23fb855b292 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:11 -0700 Subject: [PATCH 33/68] Batch layout as display nodes --- AsyncDisplayKit/Details/ASDataController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 0eab204e35..458166424f 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -169,7 +169,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInteger k = j; k < j + batchCount; k++) { - ASCellNode *node = nodes[k]; + ASDisplayNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { From 5839e5bf3eabfb24466a13fc773af9b2439c7646 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:42 -0700 Subject: [PATCH 34/68] Assert supplementary node validity before data controller use --- AsyncDisplayKit/ASCollectionView.mm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 8fba37b7b8..bfd943925a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -733,7 +733,10 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - return [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASDisplayNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); + ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); + return node; } - (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController From b58649fdcb0e010a22cdc27430ee64d994f36271 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:28:54 -0700 Subject: [PATCH 35/68] Clean up supplementary nodes in data controller store on each reload --- .../Details/ASCollectionDataController.mm | 10 +++++++++- .../Details/ASDataController+Subclasses.h | 12 ++++++++++++ AsyncDisplayKit/Details/ASDataController.mm | 10 ++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 69f041a62d..1f94ee5e5f 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -47,7 +47,13 @@ - (void)willReloadData { [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { - LOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes); + // Remove everything that existed before the reload, now that we're ready to insert replacements + NSArray *indexPaths = [self indexPathsForEditingNodesOfKind:kind]; + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + + NSArray *editingNodes = [self editingNodesOfKind:kind]; + NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; + [self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil]; // Insert each section NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; @@ -60,6 +66,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; + _pendingNodes[kind] = [NSArray array]; + _pendingIndexPaths[kind] = [NSArray array]; }]; } diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 03e1e32089..d1ae9507cd 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -32,9 +32,21 @@ /** * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. + * + * @discussion Invoked on the editing transaction queue. */ - (void)willReloadData; +/** + * Provides a collection of index paths for nodes of the given kind that are currently in the editing store + */ +- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind; + +/** + * Read-only access to the underlying editing nodes of the given kind + */ +- (NSArray *)editingNodesOfKind:(NSString *)kind; + /** * Read only access to the underlying completed nodes of the given kind */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 458166424f..41475ec050 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -786,6 +786,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Querying (Subclass API) +- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind +{ + return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array]; +} + +- (NSArray *)editingNodesOfKind:(NSString *)kind +{ + return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSArray array]; +} + - (NSArray *)completedNodesOfKind:(NSString *)kind { return _completedNodes[kind]; From 432136740fc8a37e048b9d27685ba55a1e2dd6a8 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 09:29:23 -0700 Subject: [PATCH 36/68] Constrain flow layout header/footer size to collection view bounds --- .../Details/ASCollectionViewFlowLayoutInspector.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index 4c486d5d00..a34b6d66ca 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -44,12 +44,12 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - CGSize constrainedSize = CGSizeMake(FLT_MAX, FLT_MAX); + CGSize constrainedSize; CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { - constrainedSize.height = supplementarySize.height; + constrainedSize = CGSizeMake(_collectionView.bounds.size.width, supplementarySize.height); } else { - constrainedSize.width = supplementarySize.width; + constrainedSize = CGSizeMake(supplementarySize.height, _collectionView.bounds.size.height); } return ASSizeRangeMake(CGSizeZero, constrainedSize); } From 71966f3549d80f459a22f463e5df138c36e5ac28 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 10:14:29 -0700 Subject: [PATCH 37/68] Relayout all nodes in backing store on `relayoutAllNodes` --- AsyncDisplayKit/ASCollectionView.mm | 2 +- AsyncDisplayKit/ASTableView.mm | 2 +- AsyncDisplayKit/Details/ASDataController.h | 6 ++- AsyncDisplayKit/Details/ASDataController.mm | 43 ++++++++++++--------- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index bfd943925a..da66f48df9 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -596,7 +596,7 @@ static BOOL _isInterceptedSelector(SEL sel) _ignoreMaxSizeChange = NO; } else { [self performBatchAnimated:NO updates:^{ - [_dataController relayoutAllRows]; + [_dataController relayoutAllNodes]; } completion:nil]; } } diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index a8dc8c6f9d..e2faa2b9c0 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -405,7 +405,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { _ignoreMaxWidthChange = NO; } else { [self beginUpdates]; - [_dataController relayoutAllRows]; + [_dataController relayoutAllNodes]; [self endUpdates]; } } diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index ea4e4694a3..e605aa86d6 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -157,10 +157,12 @@ typedef NSUInteger ASDataControllerAnimationOptions; - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; /** - * Re-measures all loaded nodes. Used to respond to a change in size of the containing view + * Re-measures all loaded nodes in the backing store. + * + * @discussion Used to respond to a change in size of the containing view * (e.g. ASTableView or ASCollectionView after an orientation change). */ -- (void)relayoutAllRows; +- (void)relayoutAllNodes; - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 41475ec050..d54ca3af10 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -729,41 +729,46 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)relayoutAllRows +- (void)relayoutAllNodes { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); LOG(@"Edit Command - relayoutRows"); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; - - void (^relayoutNodesBlock)(NSMutableArray *) = ^void(NSMutableArray *nodes) { - if (!nodes.count) { - return; - } - - [self accessDataSourceWithBlock:^{ - [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; - ASSizeRange constrainedSize = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); - }]; - }]; - }]; - }; // Can't relayout right away because _completedNodes may not be up-to-date, // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedNodes // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - relayoutNodesBlock(_completedNodes[ASDataControllerRowNodeKind]); + [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + [self _relayoutNodesOfKind:kind]; + }]; }); }]; }]; } +- (void)_relayoutNodesOfKind:(NSString *)kind +{ + ASDisplayNodeAssertMainThread(); + NSArray *nodes = [self completedNodesOfKind:kind]; + if (!nodes.count) { + return; + } + + [self accessDataSourceWithBlock:^{ + [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + }]; + }]; + }]; +} + - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ From ed7a73361be9a89e159071c43dabb9e93efdc9db Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 11:06:29 -0700 Subject: [PATCH 38/68] Use simpler completed nodes loop --- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index d54ca3af10..5a73c0200c 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -741,9 +741,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // (see _layoutNodes:atIndexPaths:withAnimationOptions:). [_editingTransactionQueue addOperationWithBlock:^{ ASDisplayNodePerformBlockOnMainThread(^{ - [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + for (NSString *kind in [_completedNodes keyEnumerator]) { [self _relayoutNodesOfKind:kind]; - }]; + } }); }]; }]; From f9bba315df9ed8c4d3354ff9a1ebb372ef382c4f Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:00:13 -0700 Subject: [PATCH 39/68] Stub out subclass hooks for section insert/remove/update/moving --- .../Details/ASCollectionDataController.mm | 48 +++++++++++-- .../Details/ASDataController+Subclasses.h | 14 ++++ AsyncDisplayKit/Details/ASDataController.mm | 71 ++++++++++++++++--- 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 1f94ee5e5f..d3c91d6f7d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -28,6 +28,11 @@ NSMutableDictionary *_pendingIndexPaths; } +- (void)willPerformInitialDataLoading +{ + // TODO: Implement +} + - (void)prepareForReloadData { _pendingNodes = [NSMutableDictionary dictionary]; @@ -71,13 +76,29 @@ }]; } -- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (void)prepareInsertSections:(NSIndexSet *)sections { - if ([kind isEqualToString:ASDataControllerRowNodeKind]) { - return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; - } else { - return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; - } + // TODO: Implement +} + +- (void)willInsertSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willDeleteSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + // TODO: Implement +} + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + // TODO: Implement } - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths @@ -94,12 +115,27 @@ } } +#pragma mark - Sizing query + +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + } else { + return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } +} + +#pragma mark - External supplementary store querying + - (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; } +#pragma mark - Private Helpers + - (id)collectionDataSource { return (id)self.dataSource; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index d1ae9507cd..ea0d711f53 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -25,6 +25,8 @@ */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block; +- (void)willPerformInitialDataLoading; + /** * An opportunity for a subclass to access the data source before entering into the editing queue */ @@ -37,6 +39,18 @@ */ - (void)willReloadData; +- (void)prepareInsertSections:(NSIndexSet *)sections; + +- (void)willInsertSections:(NSIndexSet *)sections; + +- (void)willDeleteSections:(NSIndexSet *)sections; + +- (void)prepareForReloadSections:(NSIndexSet *)sections; + +- (void)willReloadSections:(NSIndexSet *)sections; + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; + /** * Provides a collection of index paths for nodes of the given kind that are currently in the editing store */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 5a73c0200c..89eab8b8e4 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -334,6 +334,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ + [self willPerformInitialDataLoading]; + NSMutableArray *indexPaths = [NSMutableArray array]; NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; @@ -404,6 +406,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Initial & Reload Data Hooks (Subclass API) + +- (void)willPerformInitialDataLoading +{ + // Implemented by subclasses +} + - (void)prepareForReloadData { // Implemented by subclasses @@ -538,7 +547,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Section Editing (External API) -- (void)insertSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); @@ -546,41 +555,47 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ + [self prepareInsertSections:sections]; + NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; - [self _populateFromDataSourceWithSectionIndexSet:indexSet mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; + [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willInsertSections:sections]; + LOG(@"Edit Transaction - insertSections: %@", indexSet); - NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count]; - for (NSUInteger i = 0; i < indexSet.count; i++) { + NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; + for (NSUInteger i = 0; i < sections.count; i++) { [sectionArray addObject:[NSMutableArray array]]; } - [self _insertSections:sectionArray atIndexSet:indexSet withAnimationOptions:animationOptions]; + [self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions]; [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions]; }]; }]; }]; } -- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - deleteSections: %@", indexSet); + LOG(@"Edit Command - deleteSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willDeleteSections:sections]; + // remove elements - LOG(@"Edit Transaction - deleteSections: %@", indexSet); - NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], indexSet); + LOG(@"Edit Transaction - deleteSections: %@", sections); + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; - [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [self _deleteSectionsAtIndexSet:sections withAnimationOptions:animationOptions]; }]; }]; } @@ -594,6 +609,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ + [self prepareForReloadSections:sections]; + NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -628,6 +645,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willMoveSection:section toSection:newSection]; + // remove elements LOG(@"Edit Transaction - moveSection"); @@ -649,6 +668,38 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +#pragma mark - Section Insertion Hooks (Subclass API) + +- (void)prepareInsertSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willInsertSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willDeleteSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)prepareForReloadSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willReloadSections:(NSIndexSet *)sections +{ + // Implemented by subclass +} + +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection +{ + // Implemented by subclass +} + #pragma mark - Row Editing (External API) - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions From d36c8a36c6ed8a75cd02e285824552a45d41e6a5 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:01:17 -0700 Subject: [PATCH 40/68] Introduce ability to populate internal supplementary nodes at specific sections --- .../Details/ASCollectionDataController.mm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index d3c91d6f7d..5108eec8b8 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -115,6 +115,19 @@ } } +- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths +{ + [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:idx]; + NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx]; + for (NSUInteger i = 0; i < rowNum; i++) { + NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i]; + [indexPaths addObject:indexPath]; + [nodes addObject:[self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath]]; + } + }]; +} + #pragma mark - Sizing query - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath From 2cc55513a49f4b27df0c7b785286fc47cf40ec00 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:01:41 -0700 Subject: [PATCH 41/68] Nil out pending store kinds after processing --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 5108eec8b8..17e5b00c98 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -71,8 +71,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; - _pendingNodes[kind] = [NSArray array]; - _pendingIndexPaths[kind] = [NSArray array]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; }]; } From e1ea2d2151d2ef6506a328dcd2fecceedecddecf Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:02:28 -0700 Subject: [PATCH 42/68] Handle supplementary node generation during insertion of new section --- .../Details/ASCollectionDataController.mm | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 17e5b00c98..98d4fb4895 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -78,12 +78,28 @@ - (void)prepareInsertSections:(NSIndexSet *)sections { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + LOG(@"Populating elements of kind: %@, for sections: %@", kind, sections); + NSMutableArray *nodes = [NSMutableArray array]; + NSMutableArray *indexPaths = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; + }]; } - (void)willInsertSections:(NSIndexSet *)sections { - // TODO: Implement + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; + for (NSUInteger i = 0; i < sections.count; i++) { + [sectionArray addObject:[NSMutableArray array]]; + } + + [self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil]; + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + }]; } - (void)willDeleteSections:(NSIndexSet *)sections From d77df45bfd55ad79d8ecd15b625974ac660c51e6 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:03:16 -0700 Subject: [PATCH 43/68] Delete supplementary nodes when section is deleted --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 98d4fb4895..7eee33fffe 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -103,6 +103,17 @@ } - (void)willDeleteSections:(NSIndexSet *)sections +{ + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); + + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + [self deleteSectionsOfKind:kind atIndexSet:sections completion:nil]; + }]; +} + +- (void)prepareForReloadSections:(NSIndexSet *)sections { // TODO: Implement } From 9235ce5ac285ff068c49c7d9bb05c953e29a8201 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:10:28 -0700 Subject: [PATCH 44/68] Support reloading supplementary nodes when sections reload --- .../Details/ASCollectionDataController.mm | 20 +++++++++++++++++-- AsyncDisplayKit/Details/ASDataController.mm | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 7eee33fffe..733594fe4f 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -99,6 +99,8 @@ [self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil]; [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; }]; } @@ -115,12 +117,26 @@ - (void)prepareForReloadSections:(NSIndexSet *)sections { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSMutableArray *nodes = [NSMutableArray array]; + NSMutableArray *indexPaths = [NSMutableArray array]; + [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; + _pendingNodes[kind] = nodes; + _pendingIndexPaths[kind] = indexPaths; + }]; } - (void)willReloadSections:(NSIndexSet *)sections { - // TODO: Implement + [_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + // reinsert the elements + [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:nil]; + _pendingNodes[kind] = nil; + _pendingIndexPaths[kind] = nil; + }]; } - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 89eab8b8e4..e4b0f81fde 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -623,6 +623,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ + [self willReloadSections:sections]; + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); From 3f0092435afadcb43fbf35640dd2c6a1e439d1da Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 6 Oct 2015 12:17:11 -0700 Subject: [PATCH 45/68] Add support to move supplementary nodes with section position changes --- .../Details/ASCollectionDataController.mm | 15 ++++++++++++++- .../Details/ASDataController+Subclasses.h | 4 ++-- AsyncDisplayKit/Details/ASDataController.mm | 6 +++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 733594fe4f..e1a0327af3 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -141,7 +141,20 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - // TODO: Implement + NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; + [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], [NSIndexSet indexSetWithIndex:section]); + NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths([self editingNodesOfKind:kind], indexPaths); + [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; + + // update the section of indexpaths + NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection]; + NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { + [updatedIndexPaths addObject:[sectionIndexPath indexPathByAddingIndex:[indexPath indexAtPosition:indexPath.length - 1]]]; + }]; + [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; + }]; } - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index ea0d711f53..1bb3615e7e 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -59,12 +59,12 @@ /** * Read-only access to the underlying editing nodes of the given kind */ -- (NSArray *)editingNodesOfKind:(NSString *)kind; +- (NSMutableArray *)editingNodesOfKind:(NSString *)kind; /** * Read only access to the underlying completed nodes of the given kind */ -- (NSArray *)completedNodesOfKind:(NSString *)kind; +- (NSMutableArray *)completedNodesOfKind:(NSString *)kind; /** * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index e4b0f81fde..85b4ab1925 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -849,12 +849,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array]; } -- (NSArray *)editingNodesOfKind:(NSString *)kind +- (NSMutableArray *)editingNodesOfKind:(NSString *)kind { - return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSArray array]; + return _editingNodes[kind] != nil ? _editingNodes[kind] : [NSMutableArray array]; } -- (NSArray *)completedNodesOfKind:(NSString *)kind +- (NSMutableArray *)completedNodesOfKind:(NSString *)kind { return _completedNodes[kind]; } From 8a77072e3ce1a796cce6a3632eb55b4aeba6b2c1 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 15:42:28 -0700 Subject: [PATCH 46/68] Add tests for flow layout inspector section calculation --- AsyncDisplayKit.xcodeproj/project.pbxproj | 4 + ...ASCollectionViewFlowLayoutInspectorTests.m | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 6e105257a3..f096375ce4 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 251B8EF91BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ASSET_TAGS = (); }; }; 251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; settings = {ASSET_TAGS = (); }; }; 251B8EFB1BBB3D690087C538 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; settings = {ASSET_TAGS = (); }; }; + 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; settings = {ASSET_TAGS = (); }; }; 2767E9411BB19BD600EA9B77 /* ASViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = ACC945AA1BA9E7C1005E1FB8 /* ASViewController.m */; settings = {ASSET_TAGS = (); }; }; 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2911485B1A77147A005D0878 /* ASControlNodeTests.m */; }; @@ -556,6 +557,7 @@ 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = ""; }; 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = ""; }; + 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = ""; }; 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; 292C599A1A956527007E5DD6 /* ASRangeHandlerPreload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeHandlerPreload.h; sourceTree = ""; }; @@ -841,6 +843,7 @@ 058D0A37195D057000B7D73C /* ASTextNodeWordKernerTests.mm */, 058D09C6195D04C000B7D73C /* Supporting Files */, 052EE06A1A15A0D8002C6279 /* TestResources */, + 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */, ); path = AsyncDisplayKitTests; sourceTree = ""; @@ -1544,6 +1547,7 @@ 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */, ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */, 058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */, + 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */, 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */, 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */, 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */, diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m new file mode 100644 index 0000000000..ea82418df5 --- /dev/null +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -0,0 +1,81 @@ +/* Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import "ASCollectionView.h" +#import "ASCollectionViewFlowLayoutInspector.h" + +@interface TestDataSource : NSObject +@end + +@implementation TestDataSource + +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return [[ASCellNode alloc] init]; +} + +- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section +{ + return 0; +} + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return 2; +} + +@end + +@interface ASCollectionViewFlowLayoutInspectorTests : XCTestCase + +@end + +@implementation ASCollectionViewFlowLayoutInspectorTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +#pragma mark - #collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath: + + + +#pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: + +- (void)testThatItRespondsWithTheDefaultNumberOfSections +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + XCTAssert(sections == 1, @"should return 1 by default"); +} + +- (void)testThatItProvidesTheNumberOfSectionsInTheDataSource +{ + TestDataSource *dataSource = [[TestDataSource alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + XCTAssert(sections == 2, @"should return 2"); +} + +#pragma mark - #collectionView:supplementaryViewsOfKind:inSection: + +@end From 1216d748a1d5c296a4906e9ffec75dce2a706e8c Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 16:00:29 -0700 Subject: [PATCH 47/68] Test number of supplementary views in section inspector method --- ...ASCollectionViewFlowLayoutInspectorTests.m | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index ea82418df5..8bbbc7224d 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -12,10 +12,13 @@ #import "ASCollectionView.h" #import "ASCollectionViewFlowLayoutInspector.h" -@interface TestDataSource : NSObject +/** + * Test Data Source + */ +@interface InspectorTestDataSource : NSObject @end -@implementation TestDataSource +@implementation InspectorTestDataSource - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -38,6 +41,22 @@ @end +/** + * Test Delegate + */ +@interface ReferenceSizeTestDelegate : NSObject + +@end + +@implementation ReferenceSizeTestDelegate + +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section +{ + return CGSizeMake(125.0, 125.0); +} + +@end + @implementation ASCollectionViewFlowLayoutInspectorTests - (void)setUp { @@ -67,7 +86,7 @@ - (void)testThatItProvidesTheNumberOfSectionsInTheDataSource { - TestDataSource *dataSource = [[TestDataSource alloc] init]; + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; @@ -78,4 +97,44 @@ #pragma mark - #collectionView:supplementaryViewsOfKind:inSection: +- (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionHeader inSection:0]; + XCTAssert(count == 1, @"should have a header supplementary view"); +} + +- (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + XCTAssert(count == 1, @"should have a footer supplementary view"); +} + +- (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + XCTAssert(count == 0, @"should not have a footer supplementary view"); +} + @end From 8a9720dd12e94f0e46f2e4bb093b7cf659134849 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Wed, 7 Oct 2015 18:25:30 -0700 Subject: [PATCH 48/68] Add tests for constrained size method of flow layout inspector --- ...ASCollectionViewFlowLayoutInspectorTests.m | 198 +++++++++++++++++- 1 file changed, 192 insertions(+), 6 deletions(-) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 8bbbc7224d..78171d8c32 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -42,13 +42,13 @@ @end /** - * Test Delegate + * Test Delegate for Header Reference Size Implementation */ -@interface ReferenceSizeTestDelegate : NSObject +@interface HeaderReferenceSizeTestDelegate : NSObject @end -@implementation ReferenceSizeTestDelegate +@implementation HeaderReferenceSizeTestDelegate - (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section { @@ -57,6 +57,22 @@ @end +/** + * Test Delegate for Footer Reference Size Implementation + */ +@interface FooterReferenceSizeTestDelegate : NSObject + +@end + +@implementation FooterReferenceSizeTestDelegate + +- (CGSize)collectionView:(ASCollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForFooterInSection:(NSInteger)section +{ + return CGSizeMake(125.0, 125.0); +} + +@end + @implementation ASCollectionViewFlowLayoutInspectorTests - (void)setUp { @@ -71,7 +87,177 @@ #pragma mark - #collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath: +// Vertical +// Delegate implementation + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheHeaderDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + FooterReferenceSizeTestDelegate *delegate = [[FooterReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +// Size implementation + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheHeaderProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + layout.headerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionVertical; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +// Horizontal + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheHeaderDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterDelegateImplementation +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + FooterReferenceSizeTestDelegate *delegate = [[FooterReferenceSizeTestDelegate alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); +} + +// Size implementation + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheHeaderProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.headerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.width)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterProperty +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + layout.scrollDirection = UICollectionViewScrollDirectionHorizontal; + layout.footerReferenceSize = CGSizeMake(125.0, 125.0); + + CGRect rect = CGRectMake(0, 0, 100.0, 100.0); + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:rect collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); +} + +- (void)testThatItReturnsZeroSizeWhenNoReferenceSizeIsImplemented +{ + InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; + collectionView.asyncDataSource = dataSource; + collectionView.asyncDelegate = delegate; + ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; + ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; + ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeZero); + XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); +} #pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: @@ -100,7 +286,7 @@ - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; @@ -113,7 +299,7 @@ - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; layout.footerReferenceSize = CGSizeMake(125.0, 125.0); ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; @@ -127,7 +313,7 @@ - (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented { InspectorTestDataSource *dataSource = [[InspectorTestDataSource alloc] init]; - ReferenceSizeTestDelegate *delegate = [[ReferenceSizeTestDelegate alloc] init]; + HeaderReferenceSizeTestDelegate *delegate = [[HeaderReferenceSizeTestDelegate alloc] init]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; From eb5c79598c563f19ca6a91583c05856baf5b3cf7 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:25:49 -0700 Subject: [PATCH 49/68] Clean up arrangement of data controller subclassing methods --- .../Details/ASDataController+Subclasses.h | 21 ++++++------------- AsyncDisplayKit/Details/ASDataController.mm | 11 ++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1bb3615e7e..16ab8cc028 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,21 +10,6 @@ @interface ASDataController (Subclasses) -/** - * Queues the given operation until an `endUpdates` synchronize update is completed. - * - * If this method is called outside of a begin/endUpdates batch update, the block is - * executed immediately. - */ -- (void)performEditCommandWithBlock:(void (^)(void))block; - -/** - * Safely locks access to the data source and executes the given block, unlocking once complete. - * - * When `asyncDataFetching` is enabled, the block is executed on a background thread. - */ -- (void)accessDataSourceWithBlock:(dispatch_block_t)block; - - (void)willPerformInitialDataLoading; /** @@ -51,6 +36,8 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; +#pragma mark - Internal editing & completed store querying + /** * Provides a collection of index paths for nodes of the given kind that are currently in the editing store */ @@ -66,6 +53,8 @@ */ - (NSMutableArray *)completedNodesOfKind:(NSString *)kind; +#pragma mark - Node sizing + /** * Measure and layout the given nodes in optimized batches, constraining each to a given size in `constrainedSizeForNodeOfKind:atIndexPath:`. */ @@ -76,6 +65,8 @@ */ - (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +#pragma mark - Node & Section Insertion/Deletion API + /** * Inserts the given nodes of the specified kind into the backing store, calling completion on the main thread when the write finishes. */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 85b4ab1925..84e979b713 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -425,6 +425,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Data Source Access (Calling _dataSource) +/** + * Safely locks access to the data source and executes the given block, unlocking once complete. + * + * @discussion When `asyncDataFetching` is enabled, the block is executed on a background thread. + */ - (void)accessDataSourceWithBlock:(dispatch_block_t)block { if (_asyncDataFetchingEnabled) { @@ -534,6 +539,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +/** + * Queues the given operation until an `endUpdates` synchronize update is completed. + * + * If this method is called outside of a begin/endUpdates batch update, the block is + * executed immediately. + */ - (void)performEditCommandWithBlock:(void (^)(void))block { // This method needs to block the thread and synchronously perform the operation if we are not From 4fb4119f9c50e8305936366aa9708807f7b579c3 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:32:34 -0700 Subject: [PATCH 50/68] Clarify data controller subclass hooks --- AsyncDisplayKit/Details/ASDataController.mm | 47 ++++++++++----------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 84e979b713..8ae2454c78 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -406,23 +406,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -#pragma mark - Initial & Reload Data Hooks (Subclass API) - -- (void)willPerformInitialDataLoading -{ - // Implemented by subclasses -} - -- (void)prepareForReloadData -{ - // Implemented by subclasses -} - -- (void)willReloadData -{ - // Implemented by subclasses -} - #pragma mark - Data Source Access (Calling _dataSource) /** @@ -681,36 +664,52 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -#pragma mark - Section Insertion Hooks (Subclass API) + +#pragma mark - Backing store manipulation optional hooks (Subclass API) + +- (void)willPerformInitialDataLoading +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)prepareForReloadData +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} + +- (void)willReloadData +{ + // Optional template hook for subclasses (See ASDataController+Subclasses.h) +} - (void)prepareInsertSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willInsertSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willDeleteSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)prepareForReloadSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willReloadSections:(NSIndexSet *)sections { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - // Implemented by subclass + // Optional template hook for subclasses (See ASDataController+Subclasses.h) } #pragma mark - Row Editing (External API) From 848dc26a0defc84073b758f7195a712a5e8e3189 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:33:09 -0700 Subject: [PATCH 51/68] Fix hook method naming --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index e1a0327af3..f05e77780b 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -76,7 +76,7 @@ }]; } -- (void)prepareInsertSections:(NSIndexSet *)sections +- (void)prepareForInsertSections:(NSIndexSet *)sections { NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 16ab8cc028..1c3a54a2c0 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -24,7 +24,7 @@ */ - (void)willReloadData; -- (void)prepareInsertSections:(NSIndexSet *)sections; +- (void)prepareForInsertSections:(NSIndexSet *)sections; - (void)willInsertSections:(NSIndexSet *)sections; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 8ae2454c78..a04cfb3679 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -549,7 +549,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareInsertSections:sections]; + [self prepareForInsertSections:sections]; NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; @@ -682,7 +682,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Optional template hook for subclasses (See ASDataController+Subclasses.h) } -- (void)prepareInsertSections:(NSIndexSet *)sections +- (void)prepareForInsertSections:(NSIndexSet *)sections { // Optional template hook for subclasses (See ASDataController+Subclasses.h) } From 0870d50e36a040cb8d1c47b43cb85acd330314f9 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:35:27 -0700 Subject: [PATCH 52/68] Clarify layout delegate assertion messages --- AsyncDisplayKit/ASCollectionView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index da66f48df9..bcabc9c0a4 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -746,19 +746,19 @@ static BOOL _isInterceptedSelector(SEL sel) - (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; } - (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; { - ASDisplayNodeAssert(_layoutDelegate != nil, @"ASCollectionView must have a layoutDelegate for layout inspection"); + ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; } From fddb0061b07f99c9af6b4f95a24c9dd109b57d86 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:47:35 -0700 Subject: [PATCH 53/68] Use ASCellNodes for supplementary nodes --- AsyncDisplayKit/ASCollectionView.h | 2 +- AsyncDisplayKit/ASCollectionView.mm | 10 +++++----- AsyncDisplayKit/ASTableView.mm | 2 +- AsyncDisplayKit/Details/ASCollectionDataController.h | 4 ++-- AsyncDisplayKit/Details/ASCollectionDataController.mm | 2 +- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- AsyncDisplayKit/Details/ASRangeController.h | 4 ++-- AsyncDisplayKit/Details/ASRangeController.mm | 6 +++--- AsyncDisplayKit/Details/ASRangeHandlerRender.mm | 2 +- examples/ASCollectionView/Sample/ViewController.m | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 91226a5d64..338792c3d8 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -281,7 +281,7 @@ @optional -- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** * Provides the constrained size range for measuring the node at the index path. diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index bcabc9c0a4..94ff1104d7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -476,7 +476,7 @@ static BOOL _isInterceptedSelector(SEL sel) ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forNode:node]; + [_rangeController configureContentView:cell.contentView forCellNode:node]; cell.node = node; @@ -492,8 +492,8 @@ static BOOL _isInterceptedSelector(SEL sel) { NSString *identifier = [self __reuseIdentifierForKind:kind]; UICollectionReusableView *view = [self dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:identifier forIndexPath:indexPath]; - ASDisplayNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; - [_rangeController configureContentView:view forNode:node]; + ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; + [_rangeController configureContentView:view forCellNode:node]; return view; } @@ -731,9 +731,9 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - ASCollectionViewDataControllerSource Supplementary view support -- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - ASDisplayNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); return node; diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index e2faa2b9c0..314d7664c6 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -530,7 +530,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) { } ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - [_rangeController configureContentView:cell.contentView forNode:node]; + [_rangeController configureContentView:cell.contentView forCellNode:node]; cell.node = node; cell.backgroundColor = node.backgroundColor; diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index 08f8775270..9c2aa46b68 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -17,7 +17,7 @@ @protocol ASCollectionDataControllerSource -- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** The constrained size range for layout. @@ -34,6 +34,6 @@ @interface ASCollectionDataController : ASDataController -- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; +- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; @end \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index f05e77780b..13bf7ae915 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -197,7 +197,7 @@ #pragma mark - External supplementary store querying -- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { ASDisplayNodeAssertMainThread(); return [self completedNodesOfKind:kind][indexPath.section][indexPath.item]; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index a04cfb3679..552bd1292a 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -169,7 +169,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ for (NSUInteger k = j; k < j + batchCount; k++) { - ASDisplayNode *node = nodes[k]; + ASCellNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { @@ -928,7 +928,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASDisplayNodeAssertMainThread(); [_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) { [nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) { - [section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) { + [section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) { if (node.isNodeLoaded) { if (node.layerBacked) { [node.layer removeFromSuperlayer]; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index a7fc3e54df..cf4cc37354 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -41,9 +41,9 @@ * * @param contentView UIView to add a (sized) node's view to. * - * @param node The node to be added. Often an ASCellNode. + * @param cellNode The cell node to be added. */ -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node; +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; /** * Delegate and ultimate data source. Must not be nil. diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 663f9bea1c..266fb1a855 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -48,7 +48,7 @@ #pragma mark - View manipulation -- (void)moveNode:(ASDisplayNode *)node toView:(UIView *)view +- (void)moveCellNode:(ASCellNode *)node toView:(UIView *)view { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssert(node, @"Cannot move a nil node to a view"); @@ -158,7 +158,7 @@ return rangeType == ASLayoutRangeTypeRender; } -- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node +- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node { if (node.view.superview == contentView) { // this content view is already correctly configured @@ -170,7 +170,7 @@ [view removeFromSuperview]; } - [self moveNode:node toView:contentView]; + [self moveCellNode:node toView:contentView]; } diff --git a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm index 0b6efd8568..0363abba2f 100644 --- a/AsyncDisplayKit/Details/ASRangeHandlerRender.mm +++ b/AsyncDisplayKit/Details/ASRangeHandlerRender.mm @@ -64,7 +64,7 @@ // This happens if the UITableViewCell is reused after scrolling offscreen. Because the node has already been given the opportunity to display, we do not // proactively re-host it within the workingWindow (improving efficiency). Some time later, it may fall outside the working range, in which case calling // -recursivelyClearContents is critical. If the user scrolls back and it is re-hosted in a UITableViewCell, the content will still exist as it is not cleared - // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forNode:]. + // by simply being removed from the cell. The code that usually triggers this condition is the -removeFromSuperview in -[ASRangeController configureContentView:forCellNode:]. // Condition #4 is suboptimal in some cases, as it is conceivable that memory warnings could trigger clearing content that is inside the working range. However, enforcing the // preservation of this content could result in the app being killed, which is not likely preferable over briefly seeing placeholders in the event the user scrolls backwards. // Nonetheless, future changes to the implementation will likely eliminate this behavior to simplify debugging and extensibility of working range functionality. diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 9587bea933..d17929e7ae 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -78,7 +78,7 @@ return node; } -- (ASDisplayNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { NSString *text = [kind isEqualToString:UICollectionElementKindSectionHeader] ? @"Header" : @"Footer"; SupplementaryNode *node = [[SupplementaryNode alloc] initWithText:text]; From a9023dd400039f3eb9b2bb291b82b8e804a82a9d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 10:56:39 -0700 Subject: [PATCH 54/68] Fix layout of example supplementary nodes --- examples/ASCollectionView/Sample/SupplementaryNode.h | 2 +- examples/ASCollectionView/Sample/SupplementaryNode.m | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.h b/examples/ASCollectionView/Sample/SupplementaryNode.h index e64ac9e3f6..f75c929684 100644 --- a/examples/ASCollectionView/Sample/SupplementaryNode.h +++ b/examples/ASCollectionView/Sample/SupplementaryNode.h @@ -11,7 +11,7 @@ #import -@interface SupplementaryNode : ASDisplayNode +@interface SupplementaryNode : ASCellNode - (instancetype)initWithText:(NSString *)text; diff --git a/examples/ASCollectionView/Sample/SupplementaryNode.m b/examples/ASCollectionView/Sample/SupplementaryNode.m index baddef62c5..ca5579e9a6 100644 --- a/examples/ASCollectionView/Sample/SupplementaryNode.m +++ b/examples/ASCollectionView/Sample/SupplementaryNode.m @@ -36,6 +36,7 @@ static CGFloat kInsets = 15.0; - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { ASCenterLayoutSpec *center = [[ASCenterLayoutSpec alloc] init]; + center.centeringOptions = ASCenterLayoutSpecCenteringXY; center.child = _textNode; UIEdgeInsets insets = UIEdgeInsetsMake(kInsets, kInsets, kInsets, kInsets); return [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:center]; From ba72c737796690cacd661900a08d31028c4957a2 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 11:28:01 -0700 Subject: [PATCH 55/68] Clarify default value behavior of the layoutDelegate for flow layouts --- AsyncDisplayKit/ASCollectionView.mm | 6 +++--- .../ASCollectionViewFlowLayoutInspector.m | 5 +++-- AsyncDisplayKitTests/ASCollectionViewTests.m | 21 +++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 94ff1104d7..385ca7faf7 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -228,7 +228,9 @@ static BOOL _isInterceptedSelector(SEL sel) // Register the default layout inspector delegate for flow layouts, custom layouts // will need to roll their own ASCollectionViewLayoutInspecting implementation. - _layoutDelegate = [self flowLayoutInspector]; + if ([layout asdk_isFlowLayout]) { + _layoutDelegate = [self flowLayoutInspector]; + } _registeredSupplementaryKinds = [NSMutableArray array]; @@ -249,8 +251,6 @@ static BOOL _isInterceptedSelector(SEL sel) /** * A layout inspector implementation specific for the sizing behavior of UICollectionViewFlowLayouts - * - * @discussion Will return nil if the current layout is not a flow layout */ - (ASCollectionViewFlowLayoutInspector *)flowLayoutInspector { diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index a34b6d66ca..efdb26bdaf 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -21,11 +21,12 @@ - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout { + self = [super init]; + if (flowLayout == nil) { return nil; } - - self = [super init]; + if (self != nil) { self.collectionView = collectionView; _layout = flowLayout; diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index 3b4d661514..1993d0734b 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -6,7 +6,8 @@ // #import -#import +#import "ASCollectionView.h" +#import "ASCollectionViewFlowLayoutInspector.h" @interface ASCollectionViewTestDelegate : NSObject @@ -79,7 +80,23 @@ @implementation ASCollectionViewTests -- (void)DISABLED_testCollectionViewController { +- (void)testThatItSetsALayoutInspectorForFlowLayouts +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + XCTAssert(collectionView.layoutDelegate != nil, @"should automatically set a layout delegate for flow layouts"); + XCTAssert([collectionView.layoutDelegate isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default"); +} + +- (void)testThatItDoesNotSetALayoutInspectorForCustomLayouts +{ + UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts"); +} + +- (void)DISABLED_testCollectionViewController +{ ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; From c8a79c5cfb72f13975b8fa81f625487cad3c4c72 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sun, 11 Oct 2015 11:29:54 -0700 Subject: [PATCH 56/68] Ignore coverage analysis files in master --- .gitignore | 2 ++ ObjectiveC.gcno | Bin 924 -> 0 bytes QuartzCore.gcno | Bin 924 -> 0 bytes 3 files changed, 2 insertions(+) delete mode 100644 ObjectiveC.gcno delete mode 100644 QuartzCore.gcno diff --git a/.gitignore b/.gitignore index aee909766f..84de6767f4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ docs/.sass-cache *.lock *.gcov +*.gcno +*.gcda diff --git a/ObjectiveC.gcno b/ObjectiveC.gcno deleted file mode 100644 index 05bc771aa773306a2dac55e20902adbb6fb7cbe7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 924 zcmd1LOHS7^GB9a6bMz1c0|TQBkN|-zatj`?18MDO$Kc@PqWqkku*}qQg`C97)FS7c z#Ny&u1_lA3ynbkLYEiL%ZemexYEDsprM_cvWnQvNW^n;fsdr`xCLd~?Uw%reUM_Npb<{_;@1& jY+)6jl#`#F9iN$3mYNpb<{_;@1& jY+)6jl#`#F9iN$3mY Date: Sun, 11 Oct 2015 12:38:53 -0700 Subject: [PATCH 57/68] Homogenize cell node measuring --- AsyncDisplayKit/Details/ASDataController.mm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 552bd1292a..1e67c329c1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -103,6 +103,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Cell Layout +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); +} + /* * FIXME: Shouldn't this method, as well as `_layoutNodes:atIndexPaths:withAnimationOptions:` use the word "measure" instead? * @@ -116,8 +122,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; ASCellNode *node = nodes[idx]; if (node.isNodeLoaded) { ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + [self _layoutNode:node withConstrainedSize:constrainedSize]; } }]; } @@ -173,9 +178,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // Only measure nodes whose views aren't loaded, since we're in the background. // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: if (!node.isNodeLoaded) { - ASSizeRange constrainedSize = nodeBoundSizes[k]; - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); + [self _layoutNode:node withConstrainedSize:nodeBoundSizes[k]]; } } }); From 952a66a9243b8cbc221b73d9a60af65e4af5b332 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 10:28:39 -0700 Subject: [PATCH 58/68] Remove initial data loading subclass hook --- AsyncDisplayKit/Details/ASCollectionDataController.mm | 5 ----- AsyncDisplayKit/Details/ASDataController+Subclasses.h | 2 -- AsyncDisplayKit/Details/ASDataController.mm | 11 ++--------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 13bf7ae915..081d44585d 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -28,11 +28,6 @@ NSMutableDictionary *_pendingIndexPaths; } -- (void)willPerformInitialDataLoading -{ - // TODO: Implement -} - - (void)prepareForReloadData { _pendingNodes = [NSMutableDictionary dictionary]; diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 1c3a54a2c0..6f930ca23b 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,8 +10,6 @@ @interface ASDataController (Subclasses) -- (void)willPerformInitialDataLoading; - /** * An opportunity for a subclass to access the data source before entering into the editing queue */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 1e67c329c1..f3901a7ae5 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -110,9 +110,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } /* - * FIXME: Shouldn't this method, as well as `_layoutNodes:atIndexPaths:withAnimationOptions:` use the word "measure" instead? + * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. * - * Once nodes have loaded their views, we can't layout in the background so this is a chance + * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance * to do so immediately on the main thread. */ - (void)_layoutNodesWithMainThreadAffinity:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { @@ -337,8 +337,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); [self accessDataSourceWithBlock:^{ - [self willPerformInitialDataLoading]; - NSMutableArray *indexPaths = [NSMutableArray array]; NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self]; @@ -670,11 +668,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Backing store manipulation optional hooks (Subclass API) -- (void)willPerformInitialDataLoading -{ - // Optional template hook for subclasses (See ASDataController+Subclasses.h) -} - - (void)prepareForReloadData { // Optional template hook for subclasses (See ASDataController+Subclasses.h) From ee0cc2001a37a91884a79c5c2bce908438b51c41 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 16:06:52 -0700 Subject: [PATCH 59/68] Add support for loaded node layout for supplementary views --- AsyncDisplayKit/ASCollectionView.mm | 1 - .../Details/ASCollectionDataController.mm | 13 +++- .../Details/ASDataController+Subclasses.h | 8 +++ AsyncDisplayKit/Details/ASDataController.mm | 65 +++++++++---------- .../ASCollectionView/Sample/ViewController.m | 7 +- 5 files changed, 56 insertions(+), 38 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 385ca7faf7..c16715b4eb 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -735,7 +735,6 @@ static BOOL _isInterceptedSelector(SEL sel) { ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node"); - ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded"); return node; } diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 081d44585d..0bbc2c502c 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -41,6 +41,9 @@ [self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } @@ -66,8 +69,8 @@ [self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) { [self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil]; }]; - _pendingNodes[kind] = nil; - _pendingIndexPaths[kind] = nil; + [_pendingNodes removeObjectForKey:kind]; + [_pendingIndexPaths removeObjectForKey:kind]; }]; } @@ -81,6 +84,9 @@ [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } @@ -119,6 +125,9 @@ [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; _pendingNodes[kind] = nodes; _pendingIndexPaths[kind] = indexPaths; + + // Measure loaded nodes before leaving the main thread + [self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths]; }]; } diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 6f930ca23b..018e58348a 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -58,6 +58,14 @@ */ - (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock; +/* + * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. + * + * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance + * to do so immediately on the main thread. + */ +- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths; + /** * Provides the size range for a specific node during the layout process. */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index f3901a7ae5..24dcd68d2c 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -103,30 +103,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Cell Layout -- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize -{ - [node measureWithSizeRange:constrainedSize]; - node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); -} - -/* - * Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes. - * - * @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance - * to do so immediately on the main thread. - */ -- (void)_layoutNodesWithMainThreadAffinity:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { - NSAssert(NSThread.isMainThread, @"Main thread layout must be on the main thread."); - - [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { - ASCellNode *node = nodes[idx]; - if (node.isNodeLoaded) { - ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath]; - [self _layoutNode:node withConstrainedSize:constrainedSize]; - } - }]; -} - - (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; @@ -141,6 +117,27 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } } +- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths { + NSAssert(NSThread.isMainThread, @"Main thread layout must be on the main thread."); + + [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) { + ASCellNode *node = nodes[idx]; + if (node.isNodeLoaded) { + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + [self _layoutNode:node withConstrainedSize:constrainedSize]; + } + }]; +} + +/** + * Measure and layout the given node with the constrained size range. + */ +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + [node measureWithSizeRange:constrainedSize]; + node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); +} + /** * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. */ @@ -176,7 +173,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; for (NSUInteger k = j; k < j + batchCount; k++) { ASCellNode *node = nodes[k]; // Only measure nodes whose views aren't loaded, since we're in the background. - // We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity: + // We should already have measured loaded nodes before we left the main thread, using layoutLoadedNodes:ofKind:atIndexPaths: if (!node.isNodeLoaded) { [self _layoutNode:node withConstrainedSize:nodeBoundSizes[k]]; } @@ -371,7 +368,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; // Allow subclasses to perform setup before going into the edit transaction [self prepareForReloadData]; @@ -550,14 +547,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareForInsertSections:sections]; - NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; + + [self prepareForInsertSections:sections]; [_editingTransactionQueue addOperationWithBlock:^{ [self willInsertSections:sections]; @@ -604,8 +601,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ - [self prepareForReloadSections:sections]; - NSMutableArray *updatedNodes = [NSMutableArray array]; NSMutableArray *updatedIndexPaths = [NSMutableArray array]; [self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths]; @@ -615,7 +610,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; // at this time. Thus _editingNodes could be empty and crash in ASIndexPathsForMultidimensional[...] // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths]; + [self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths]; + + [self prepareForReloadSections:sections]; [_editingTransactionQueue addOperationWithBlock:^{ [self willReloadSections:sections]; @@ -727,7 +724,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; + [self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - insertRows: %@", indexPaths); @@ -777,7 +774,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; } // Measure nodes whose views are loaded before we leave the main thread - [self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths]; + [self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths]; [_editingTransactionQueue addOperationWithBlock:^{ LOG(@"Edit Transaction - reloadRows: %@", indexPaths); diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index d17929e7ae..01350da1a3 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -92,7 +92,12 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return 300; + return 10; +} + +- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView +{ + return 100; } - (void)collectionViewLockDataSource:(ASCollectionView *)collectionView From 8ac02a6eaf1836623ea2c352fd0a0d19f887d534 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 17:27:53 -0700 Subject: [PATCH 60/68] Document public supplementary node methods --- AsyncDisplayKit/ASCollectionView.h | 20 ++++ .../Details/ASDataController+Subclasses.h | 111 ++++++++++++++---- 2 files changed, 107 insertions(+), 24 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 338792c3d8..1bd6344130 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -126,6 +126,16 @@ */ - (void)reloadData; +/** + * Registers the given kind of supplementary node for use in creating node-backed supplementary views. + * + * @param kind The kind of supplementary node that will be requested through the data source. + * + * @discussion Use this method to register support for the use of supplementary nodes in place of the default + * `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:` + * 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; /** @@ -281,6 +291,13 @@ @optional +/** + * Asks the collection view to provide a supplementary node to display in the collection view. + * + * @param collectionView An object representing the collection view requesting this information. + * @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; /** @@ -355,6 +372,9 @@ @end +/** + * Defines methods that let you coordinate with a `UICollectionViewFlowLayout` in combination with an `ASCollectionView`. + */ @protocol ASCollectionViewDelegateFlowLayout @optional diff --git a/AsyncDisplayKit/Details/ASDataController+Subclasses.h b/AsyncDisplayKit/Details/ASDataController+Subclasses.h index 018e58348a..3e33449389 100644 --- a/AsyncDisplayKit/Details/ASDataController+Subclasses.h +++ b/AsyncDisplayKit/Details/ASDataController+Subclasses.h @@ -10,30 +10,6 @@ @interface ASDataController (Subclasses) -/** - * An opportunity for a subclass to access the data source before entering into the editing queue - */ -- (void)prepareForReloadData; - -/** - * Subclasses can override this to reload data after the abstract data controller deletes its old data and before it reloads the new. - * - * @discussion Invoked on the editing transaction queue. - */ -- (void)willReloadData; - -- (void)prepareForInsertSections:(NSIndexSet *)sections; - -- (void)willInsertSections:(NSIndexSet *)sections; - -- (void)willDeleteSections:(NSIndexSet *)sections; - -- (void)prepareForReloadSections:(NSIndexSet *)sections; - -- (void)willReloadSections:(NSIndexSet *)sections; - -- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; - #pragma mark - Internal editing & completed store querying /** @@ -93,4 +69,91 @@ */ - (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock; +#pragma mark - Data Manipulation Hooks + +/** + * Notifies the subclass to perform any work needed before the data controller is reloaded entirely + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + */ + - (void)prepareForReloadData; + +/** + * Notifies the subclass that the data controller is about to reload its data entirely + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform new node creation like supplementary views + * or header/footer nodes. + */ +- (void)willReloadData; + +/** + * Notifies the subclass to perform setup before sections are inserted in the data controller + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param sections Indices of sections to be inserted + */ +- (void)prepareForInsertSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will insert new sections at the given position + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be inserted + */ +- (void)willInsertSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will delete sections at the given positions + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be deleted + */ +- (void)willDeleteSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass to perform any work needed before the given sections will be reloaded. + * + * @discussion This method will be performed before the data controller enters its editing queue, usually on the main + * thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or + * data stores before entering into editing the backing store on a background thread. + * + * @param sections Indices of sections to be reloaded + */ +- (void)prepareForReloadSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will reload the sections in the given index set + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param sections Indices of sections to be reloaded + */ +- (void)willReloadSections:(NSIndexSet *)sections; + +/** + * Notifies the subclass that the data controller will move a section to a new position + * + * @discussion This method will be performed on the data controller's editing background queue before the parent's + * concrete implementation. This is a great place to perform any additional transformations like supplementary views + * or header/footer nodes. + * + * @param section Index of current section position + * @param newSection Index of new section position + */ +- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection; + @end From e2bbde2a4e8ba2fb299c377cb7826cbf5f56d1bc Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:19:40 -0700 Subject: [PATCH 61/68] Clean up usage of internal flow layout inspector --- AsyncDisplayKit/ASCollectionView.mm | 11 +++- .../ASCollectionViewFlowLayoutInspector.h | 3 +- .../ASCollectionViewFlowLayoutInspector.m | 56 ++++++++++--------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index c16715b4eb..36d700a0c6 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -337,7 +337,16 @@ static BOOL _isInterceptedSelector(SEL sel) _asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0); } - _flowLayoutInspector.collectionView = self; + [_flowLayoutInspector cacheSelectorsForCollectionView:self]; +} + +- (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout +{ + [super setCollectionViewLayout:collectionViewLayout]; + if ([collectionViewLayout asdk_isFlowLayout]) { + _flowLayoutInspector = nil; + _layoutDelegate = [self flowLayoutInspector]; + } } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 94d65e2117..4e4f266cfd 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -34,10 +34,11 @@ @interface ASCollectionViewFlowLayoutInspector : NSObject @property (nonatomic, weak) UICollectionViewFlowLayout *layout; -@property (nonatomic, weak) ASCollectionView *collectionView; - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; +- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView; + - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index efdb26bdaf..f9ea2f8293 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -19,26 +19,30 @@ #pragma mark - Accessors -- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout +- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout; { - self = [super init]; + self = [super init]; - if (flowLayout == nil) { - return nil; - } - - if (self != nil) { - self.collectionView = collectionView; - _layout = flowLayout; - } - return self; + if (flowLayout == nil) { + return nil; + } + + if (self != nil) { + [self cacheSelectorForCollectionView:collectionView]; + _layout = flowLayout; + } + return self; } -- (void)setCollectionView:(ASCollectionView *)collectionView +- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView { - _collectionView = collectionView; - _delegateImplementsReferenceSizeForHeader = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; - _delegateImplementsReferenceSizeForFooter = [[self layoutDelegate] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + if (collectionView == nil) { + _delegateImplementsReferenceSizeForHeader = nil; + _delegateImplementsReferenceSizeForFooter = nil; + } else { + _delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)]; + _delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)]; + } } #pragma mark - ASCollectionViewLayoutInspecting @@ -46,11 +50,11 @@ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { CGSize constrainedSize; - CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section]; + CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section collectionView:collectionView]; if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) { - constrainedSize = CGSizeMake(_collectionView.bounds.size.width, supplementarySize.height); + constrainedSize = CGSizeMake(collectionView.bounds.size.width, supplementarySize.height); } else { - constrainedSize = CGSizeMake(supplementarySize.height, _collectionView.bounds.size.height); + constrainedSize = CGSizeMake(supplementarySize.height, collectionView.bounds.size.height); } return ASSizeRangeMake(CGSizeZero, constrainedSize); } @@ -66,22 +70,22 @@ - (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section { - return [self layoutHasSupplementaryViewOfKind:kind inSection:section] ? 1 : 0; + return [self layoutHasSupplementaryViewOfKind:kind inSection:section collectionView:collectionView] ? 1 : 0; } #pragma mark - Private helpers -- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView { if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { if (_delegateImplementsReferenceSizeForHeader) { - return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForHeaderInSection:section]; + return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:section]; } else { return [self.layout headerReferenceSize]; } } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) { if (_delegateImplementsReferenceSizeForFooter) { - return [[self layoutDelegate] collectionView:_collectionView layout:_layout referenceSizeForFooterInSection:section]; + return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:section]; } else { return [self.layout footerReferenceSize]; } @@ -90,9 +94,9 @@ } } -- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section +- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView { - CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section]; + CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section collectionView:collectionView]; if ([self usedLayoutValueForSize:size] > 0) { return YES; } else { @@ -109,9 +113,9 @@ } } -- (id)layoutDelegate +- (id)delegateForCollectionView:(ASCollectionView *)collectionView { - return (id)_collectionView.asyncDelegate; + return (id)collectionView.asyncDelegate; } @end From 0446902c1760934c14900cf64a874543a3131a1c Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:23:13 -0700 Subject: [PATCH 62/68] Remove supplementary view assertion --- AsyncDisplayKit/ASCollectionView.mm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 36d700a0c6..b7456be836 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -306,10 +306,6 @@ static BOOL _isInterceptedSelector(SEL sel) _asyncDataSourceImplementsConstrainedSizeForNode = NO; } else { _asyncDataSource = asyncDataSource; - // TODO: Support supplementary views with ASCollectionView. - if ([_asyncDataSource respondsToSelector:@selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:)]) { - ASDisplayNodeAssert(NO, @"ASCollectionView is planned to support supplementary views by September 2015. You can work around this issue by using standard items."); - } _proxyDataSource = [[_ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self]; super.dataSource = (id)_proxyDataSource; _asyncDataSourceImplementsConstrainedSizeForNode = ([_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] ? 1 : 0); From 772e9b95a4ade69a36fc9bc4a59a133b3f41c292 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:41:05 -0700 Subject: [PATCH 63/68] Document layout delegate on ASCollectionView --- AsyncDisplayKit/ASCollectionView.h | 9 +++++++-- AsyncDisplayKit/ASCollectionView.mm | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.h b/AsyncDisplayKit/ASCollectionView.h index 1bd6344130..14ec90f894 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/AsyncDisplayKit/ASCollectionView.h @@ -63,7 +63,7 @@ * * @param asyncDataFetchingEnabled Enable the data fetching in async mode. * - * @discussion If asyncDataFetching is enabled, the `AScollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and + * @discussion If asyncDataFetching is enabled, the `ASCollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and * `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically * from calling thread. * Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for @@ -83,7 +83,12 @@ /** * Optional introspection object for the collection view's layout. * - * TODO: Discuss more about this delegate + * @discussion Since supplementary and decoration views are controlled by the collection view's layout, this object + * is used as a bridge to provide information to the internal data controller about the existence of these views and + * their associated index paths. For collection views using `UICollectionViewFlowLayout`, a default inspector + * implementation `ASCollectionViewFlowLayoutInspector` is created and set on this property by default. Custom + * collection view layout subclasses will need to provide their own implementation of an inspector object for their + * supplementary views to be compatible with `ASCollectionView`'s supplementary node support. */ @property (nonatomic, weak) id layoutDelegate; diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index b7456be836..ccb1765c91 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -226,8 +226,8 @@ static BOOL _isInterceptedSelector(SEL sel) // and should not trigger a relayout. _ignoreMaxSizeChange = CGSizeEqualToSize(_maxSizeForNodesConstrainedSize, CGSizeZero); - // Register the default layout inspector delegate for flow layouts, custom layouts - // will need to roll their own ASCollectionViewLayoutInspecting implementation. + // Register the default layout inspector delegate for flow layouts only, custom layouts + // will need to roll their own ASCollectionViewLayoutInspecting implementation and set a layout delegate if ([layout asdk_isFlowLayout]) { _layoutDelegate = [self flowLayoutInspector]; } From 12194199cd028238e561568b6640ea71d317f5a6 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:48:30 -0700 Subject: [PATCH 64/68] Clean up layout delegate protocol API --- AsyncDisplayKit/ASCollectionView.mm | 8 ++++---- .../Details/ASCollectionDataController.h | 4 ++-- .../Details/ASCollectionDataController.mm | 8 ++++---- .../Details/ASCollectionViewFlowLayoutInspector.h | 12 +++--------- .../Details/ASCollectionViewFlowLayoutInspector.m | 6 +++--- .../ASCollectionViewFlowLayoutInspectorTests.m | 14 +++++++------- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index ccb1765c91..b312b86c10 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -754,16 +754,16 @@ static BOOL _isInterceptedSelector(SEL sel) return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; } -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self supplementaryViewsOfKind:kind inSection:section]; + return [_layoutDelegate collectionView:self supplementaryNodesOfKind:kind inSection:section]; } -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; { ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryKind:kind]; + return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind]; } #pragma mark - ASRangeControllerDelegate. diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.h b/AsyncDisplayKit/Details/ASCollectionDataController.h index 9c2aa46b68..54ddfbb810 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.h +++ b/AsyncDisplayKit/Details/ASCollectionDataController.h @@ -26,9 +26,9 @@ - (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; +- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; @end diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index 0bbc2c502c..ca5a48a84c 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -59,7 +59,7 @@ [self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil]; // Insert each section - NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind]; NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount]; for (int i = 0; i < sectionCount; i++) { [sections addObject:[[NSMutableArray alloc] init]]; @@ -163,10 +163,10 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { - NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind]; + NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind]; for (NSUInteger i = 0; i < sectionCount; i++) { NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i]; - NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:i]; + NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:i]; for (NSUInteger j = 0; j < rowCount; j++) { NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j]; [indexPaths addObject:indexPath]; @@ -178,7 +178,7 @@ - (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths { [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { - NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryViewsOfKind:kind inSection:idx]; + NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:idx]; NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx]; for (NSUInteger i = 0; i < rowNum; i++) { NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i]; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h index 4e4f266cfd..5aab03e012 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.h @@ -15,19 +15,19 @@ @protocol ASCollectionViewLayoutInspecting /** - * Asks the inspector + * Asks the inspector to provide a constrained size range for the given supplementary node. */ - (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; /** * Asks the inspector for the number of supplementary sections in the collection view for the given kind. */ -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind; /** * Asks the inspector for the number of supplementary views for the given kind in the specified section. */ -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; @end @@ -39,10 +39,4 @@ - (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView; -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind; - -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section; - @end diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index f9ea2f8293..e68020490d 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -28,7 +28,7 @@ } if (self != nil) { - [self cacheSelectorForCollectionView:collectionView]; + [self cacheSelectorsForCollectionView:collectionView]; _layout = flowLayout; } return self; @@ -59,7 +59,7 @@ return ASSizeRangeMake(CGSizeZero, constrainedSize); } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind +- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind { if ([collectionView.asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) { return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView]; @@ -68,7 +68,7 @@ } } -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section +- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { return [self layoutHasSupplementaryViewOfKind:kind inSection:section collectionView:collectionView] ? 1 : 0; } diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 78171d8c32..53c0956c36 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -259,14 +259,14 @@ XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); } -#pragma mark - #collectionView:numberOfSectionsForSupplementaryKind: +#pragma mark - #collectionView:numberOfSectionsForSupplementaryNodeOfKind: - (void)testThatItRespondsWithTheDefaultNumberOfSections { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 1, @"should return 1 by default"); } @@ -277,11 +277,11 @@ ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:NO]; collectionView.asyncDataSource = dataSource; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryKind:UICollectionElementKindSectionHeader]; + NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 2, @"should return 2"); } -#pragma mark - #collectionView:supplementaryViewsOfKind:inSection: +#pragma mark - #collectionView:supplementaryNodesOfKind:inSection: - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheDelegate { @@ -292,7 +292,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionHeader inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionHeader inSection:0]; XCTAssert(count == 1, @"should have a header supplementary view"); } @@ -306,7 +306,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 1, @"should have a footer supplementary view"); } @@ -319,7 +319,7 @@ collectionView.asyncDataSource = dataSource; collectionView.asyncDelegate = delegate; ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; - NSUInteger count = [inspector collectionView:collectionView supplementaryViewsOfKind:UICollectionElementKindSectionFooter inSection:0]; + NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 0, @"should not have a footer supplementary view"); } From 9a37538582457e25c27bfa31b627f9dfb4ca240d Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 18:59:27 -0700 Subject: [PATCH 65/68] Clean up and test registration of internal supplementary node kinds --- .../Details/ASCollectionDataController.mm | 20 +++++++++---------- AsyncDisplayKitTests/ASCollectionViewTests.m | 15 ++++++++++++++ 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/AsyncDisplayKit/Details/ASCollectionDataController.mm b/AsyncDisplayKit/Details/ASCollectionDataController.mm index ca5a48a84c..45f1d3af37 100644 --- a/AsyncDisplayKit/Details/ASCollectionDataController.mm +++ b/AsyncDisplayKit/Details/ASCollectionDataController.mm @@ -33,8 +33,7 @@ _pendingNodes = [NSMutableDictionary dictionary]; _pendingIndexPaths = [NSMutableDictionary dictionary]; - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { LOG(@"Populating elements of kind: %@", kind); NSMutableArray *indexPaths = [NSMutableArray array]; NSMutableArray *nodes = [NSMutableArray array]; @@ -76,8 +75,7 @@ - (void)prepareForInsertSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { LOG(@"Populating elements of kind: %@, for sections: %@", kind, sections); NSMutableArray *nodes = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; @@ -107,8 +105,7 @@ - (void)willDeleteSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections); [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; @@ -118,8 +115,7 @@ - (void)prepareForReloadSections:(NSIndexSet *)sections { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSMutableArray *nodes = [NSMutableArray array]; NSMutableArray *indexPaths = [NSMutableArray array]; [self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths]; @@ -145,8 +141,7 @@ - (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection { - NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self]; - [elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { + [[self supplementaryKinds] enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL *stop) { NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths([self editingNodesOfKind:kind], indexPaths); [self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil]; @@ -209,6 +204,11 @@ #pragma mark - Private Helpers +- (NSArray *)supplementaryKinds +{ + return [self.collectionDataSource supplementaryNodeKindsInDataController:self]; +} + - (id)collectionDataSource { return (id)self.dataSource; diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.m b/AsyncDisplayKitTests/ASCollectionViewTests.m index 1993d0734b..db993713b5 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewTests.m @@ -7,6 +7,7 @@ #import #import "ASCollectionView.h" +#import "ASCollectionDataController.h" #import "ASCollectionViewFlowLayoutInspector.h" @interface ASCollectionViewTestDelegate : NSObject @@ -74,6 +75,12 @@ @end +@interface ASCollectionView (InternalTesting) + +- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController; + +@end + @interface ASCollectionViewTests : XCTestCase @end @@ -95,6 +102,14 @@ XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts"); } +- (void)testThatRegisteringASupplementaryNodeStoresItForIntrospection +{ + UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; + ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; + [collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; + XCTAssertEqualObjects([collectionView supplementaryNodeKindsInDataController:nil], @[UICollectionElementKindSectionHeader]); +} + - (void)DISABLED_testCollectionViewController { ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil]; From 31f184b32dadcab9e9363f9f77af7bdeafdade35 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 20:20:19 -0700 Subject: [PATCH 66/68] Clear collection view delegate/dataSource on test tear down --- ...ASCollectionViewFlowLayoutInspectorTests.m | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m index 53c0956c36..1230807f44 100644 --- a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m +++ b/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m @@ -108,6 +108,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterDelegateImplementation @@ -127,6 +130,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Size implementation @@ -147,6 +153,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAVerticalConstrainedSizeFromTheFooterProperty @@ -165,6 +174,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(collectionView.bounds.size.width, 125.0)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Horizontal @@ -186,6 +198,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterDelegateImplementation @@ -205,6 +220,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the values returned in the delegate implementation"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } // Size implementation @@ -225,6 +243,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.width)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsAHorizontalConstrainedSizeFromTheFooterProperty @@ -243,6 +264,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeMake(125.0, collectionView.bounds.size.height)); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a size constrained by the size set on the layout"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsZeroSizeWhenNoReferenceSizeIsImplemented @@ -257,6 +281,9 @@ ASSizeRange size = [inspector collectionView:collectionView constrainedSizeForSupplementaryNodeOfKind:UICollectionElementKindSectionFooter atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; ASSizeRange sizeCompare = ASSizeRangeMake(CGSizeZero, CGSizeZero); XCTAssert(CGSizeEqualToSize(size.min, sizeCompare.min) && CGSizeEqualToSize(size.max, sizeCompare.max), @"should have a zero size"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } #pragma mark - #collectionView:numberOfSectionsForSupplementaryNodeOfKind: @@ -268,6 +295,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 1, @"should return 1 by default"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItProvidesTheNumberOfSectionsInTheDataSource @@ -279,6 +309,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger sections = [inspector collectionView:collectionView numberOfSectionsForSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; XCTAssert(sections == 2, @"should return 2"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } #pragma mark - #collectionView:supplementaryNodesOfKind:inSection: @@ -294,6 +327,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionHeader inSection:0]; XCTAssert(count == 1, @"should have a header supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsOneWhenAValidSizeIsImplementedOnTheLayout @@ -308,6 +344,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 1, @"should have a footer supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } - (void)testThatItReturnsNoneWhenNoReferenceSizeIsImplemented @@ -321,6 +360,9 @@ ASCollectionViewFlowLayoutInspector *inspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:collectionView flowLayout:layout]; NSUInteger count = [inspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionFooter inSection:0]; XCTAssert(count == 0, @"should not have a footer supplementary view"); + + collectionView.asyncDataSource = nil; + collectionView.asyncDelegate = nil; } @end From 55c1b8f6e74917c45cf3d8716dbf09b59d236c0e Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Mon, 12 Oct 2015 23:33:06 -0700 Subject: [PATCH 67/68] Fix variable naming in debug log --- AsyncDisplayKit/Details/ASDataController.mm | 4 ++-- AsyncDisplayKit/Details/ASRangeController.mm | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 24dcd68d2c..400a1161fd 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -543,7 +543,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); - LOG(@"Edit Command - insertSections: %@", indexSet); + LOG(@"Edit Command - insertSections: %@", sections); [_editingTransactionQueue waitUntilAllOperationsAreFinished]; [self accessDataSourceWithBlock:^{ @@ -559,7 +559,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [_editingTransactionQueue addOperationWithBlock:^{ [self willInsertSections:sections]; - LOG(@"Edit Transaction - insertSections: %@", indexSet); + LOG(@"Edit Transaction - insertSections: %@", sections); NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count]; for (NSUInteger i = 0; i < sections.count; i++) { [sectionArray addObject:[NSMutableArray array]]; diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 266fb1a855..146b85897f 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -75,7 +75,7 @@ // coalesce these events -- handling them multiple times per runloop is noisy and expensive _queuedRangeUpdate = YES; - + [self performSelector:@selector(updateVisibleNodeIndexPaths) withObject:nil afterDelay:0 From 8b7ac3c37dc44e6477680fd445edb4cadfc7dd95 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Tue, 13 Oct 2015 20:45:40 -0700 Subject: [PATCH 68/68] Ensure layout controller exposes only cell item index paths to range controller --- AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm index cbed7f2bb7..9619cba111 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm +++ b/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm @@ -137,7 +137,9 @@ typedef struct ASRangeGeometry ASRangeGeometry; NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds]; for (UICollectionViewLayoutAttributes *la in layoutAttributes) { - [indexPathSet addObject:la.indexPath]; + if (la.representedElementCategory == UICollectionElementCategoryCell) { + [indexPathSet addObject:la.indexPath]; + } } return indexPathSet; }