Add support for loaded node layout for supplementary views

This commit is contained in:
Levi McCallum 2015-10-12 16:06:52 -07:00
parent 952a66a924
commit ee0cc2001a
5 changed files with 56 additions and 38 deletions

View File

@ -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;
}

View File

@ -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];
}];
}

View File

@ -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.
*/

View File

@ -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);

View File

@ -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