From afda471bd0ba1ff836c4b62fe7dbb78e2fa1c670 Mon Sep 17 00:00:00 2001 From: Levi McCallum Date: Sat, 3 Oct 2015 15:03:09 -0700 Subject: [PATCH] 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;