Skip to content

Commit ee0cc20

Browse files
author
Levi McCallum
committed
Add support for loaded node layout for supplementary views
1 parent 952a66a commit ee0cc20

File tree

5 files changed

+54
-36
lines changed

5 files changed

+54
-36
lines changed

AsyncDisplayKit/ASCollectionView.mm

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,6 @@ - (ASCellNode *)dataController:(ASCollectionDataController *)dataController supp
735735
{
736736
ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath];
737737
ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node");
738-
ASDisplayNodeAssert(!node.nodeLoaded, @"The supplementary node must not be loaded");
739738
return node;
740739
}
741740

AsyncDisplayKit/Details/ASCollectionDataController.mm

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ - (void)prepareForReloadData
4141
[self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths];
4242
_pendingNodes[kind] = nodes;
4343
_pendingIndexPaths[kind] = indexPaths;
44+
45+
// Measure loaded nodes before leaving the main thread
46+
[self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths];
4447
}];
4548
}
4649

@@ -66,8 +69,8 @@ - (void)willReloadData
6669
[self batchLayoutNodes:nodes ofKind:kind atIndexPaths:_pendingIndexPaths[kind] completion:^(NSArray *nodes, NSArray *indexPaths) {
6770
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
6871
}];
69-
_pendingNodes[kind] = nil;
70-
_pendingIndexPaths[kind] = nil;
72+
[_pendingNodes removeObjectForKey:kind];
73+
[_pendingIndexPaths removeObjectForKey:kind];
7174
}];
7275
}
7376

@@ -81,6 +84,9 @@ - (void)prepareForInsertSections:(NSIndexSet *)sections
8184
[self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths];
8285
_pendingNodes[kind] = nodes;
8386
_pendingIndexPaths[kind] = indexPaths;
87+
88+
// Measure loaded nodes before leaving the main thread
89+
[self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths];
8490
}];
8591
}
8692

@@ -119,6 +125,9 @@ - (void)prepareForReloadSections:(NSIndexSet *)sections
119125
[self _populateSupplementaryNodesOfKind:kind withSections:sections mutableNodes:nodes mutableIndexPaths:indexPaths];
120126
_pendingNodes[kind] = nodes;
121127
_pendingIndexPaths[kind] = indexPaths;
128+
129+
// Measure loaded nodes before leaving the main thread
130+
[self layoutLoadedNodes:nodes ofKind:kind atIndexPaths:indexPaths];
122131
}];
123132
}
124133

AsyncDisplayKit/Details/ASDataController+Subclasses.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@
5858
*/
5959
- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
6060

61+
/*
62+
* Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes.
63+
*
64+
* @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance
65+
* to do so immediately on the main thread.
66+
*/
67+
- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths;
68+
6169
/**
6270
* Provides the size range for a specific node during the layout process.
6371
*/

AsyncDisplayKit/Details/ASDataController.mm

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -103,42 +103,39 @@ + (NSUInteger)parallelProcessorCount
103103

104104
#pragma mark - Cell Layout
105105

106-
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
106+
- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
107107
{
108-
[node measureWithSizeRange:constrainedSize];
109-
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
108+
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
109+
110+
// Processing in batches
111+
for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) {
112+
NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize));
113+
NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange];
114+
NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange];
115+
116+
[self _layoutNodes:batchedNodes ofKind:kind atIndexPaths:batchedIndexPaths completion:completionBlock];
117+
}
110118
}
111119

112-
/*
113-
* Perform measurement and layout of loaded nodes on the main thread, skipping unloaded nodes.
114-
*
115-
* @discussion Once nodes have loaded their views, we can't layout in the background so this is a chance
116-
* to do so immediately on the main thread.
117-
*/
118-
- (void)_layoutNodesWithMainThreadAffinity:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths {
120+
- (void)layoutLoadedNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths {
119121
NSAssert(NSThread.isMainThread, @"Main thread layout must be on the main thread.");
120122

121123
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, __unused BOOL * stop) {
122124
ASCellNode *node = nodes[idx];
123125
if (node.isNodeLoaded) {
124-
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
126+
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
125127
[self _layoutNode:node withConstrainedSize:constrainedSize];
126128
}
127129
}];
128130
}
129131

130-
- (void)batchLayoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
132+
/**
133+
* Measure and layout the given node with the constrained size range.
134+
*/
135+
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
131136
{
132-
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
133-
134-
// Processing in batches
135-
for (NSUInteger i = 0; i < indexPaths.count; i += blockSize) {
136-
NSRange batchedRange = NSMakeRange(i, MIN(indexPaths.count - i, blockSize));
137-
NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange];
138-
NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange];
139-
140-
[self _layoutNodes:batchedNodes ofKind:kind atIndexPaths:batchedIndexPaths completion:completionBlock];
141-
}
137+
[node measureWithSizeRange:constrainedSize];
138+
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
142139
}
143140

144141
/**
@@ -176,7 +173,7 @@ - (void)_layoutNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSAr
176173
for (NSUInteger k = j; k < j + batchCount; k++) {
177174
ASCellNode *node = nodes[k];
178175
// Only measure nodes whose views aren't loaded, since we're in the background.
179-
// We should already have measured loaded nodes before we left the main thread, using _layoutNodesWithMainThreadAffinity:
176+
// We should already have measured loaded nodes before we left the main thread, using layoutLoadedNodes:ofKind:atIndexPaths:
180177
if (!node.isNodeLoaded) {
181178
[self _layoutNode:node withConstrainedSize:nodeBoundSizes[k]];
182179
}
@@ -371,7 +368,7 @@ - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animati
371368
[self _populateFromEntireDataSourceWithMutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
372369

373370
// Measure nodes whose views are loaded before we leave the main thread
374-
[self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths];
371+
[self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths];
375372

376373
// Allow subclasses to perform setup before going into the edit transaction
377374
[self prepareForReloadData];
@@ -550,14 +547,14 @@ - (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataContro
550547
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
551548

552549
[self accessDataSourceWithBlock:^{
553-
[self prepareForInsertSections:sections];
554-
555550
NSMutableArray *updatedNodes = [NSMutableArray array];
556551
NSMutableArray *updatedIndexPaths = [NSMutableArray array];
557552
[self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
558553

559554
// Measure nodes whose views are loaded before we leave the main thread
560-
[self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths];
555+
[self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths];
556+
557+
[self prepareForInsertSections:sections];
561558

562559
[_editingTransactionQueue addOperationWithBlock:^{
563560
[self willInsertSections:sections];
@@ -604,8 +601,6 @@ - (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataContro
604601
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
605602

606603
[self accessDataSourceWithBlock:^{
607-
[self prepareForReloadSections:sections];
608-
609604
NSMutableArray *updatedNodes = [NSMutableArray array];
610605
NSMutableArray *updatedIndexPaths = [NSMutableArray array];
611606
[self _populateFromDataSourceWithSectionIndexSet:sections mutableNodes:updatedNodes mutableIndexPaths:updatedIndexPaths];
@@ -615,7 +610,9 @@ - (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataContro
615610
// at this time. Thus _editingNodes could be empty and crash in ASIndexPathsForMultidimensional[...]
616611

617612
// Measure nodes whose views are loaded before we leave the main thread
618-
[self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths];
613+
[self layoutLoadedNodes:updatedNodes ofKind:ASDataControllerRowNodeKind atIndexPaths:updatedIndexPaths];
614+
615+
[self prepareForReloadSections:sections];
619616

620617
[_editingTransactionQueue addOperationWithBlock:^{
621618
[self willReloadSections:sections];
@@ -727,7 +724,7 @@ - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDat
727724
}
728725

729726
// Measure nodes whose views are loaded before we leave the main thread
730-
[self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths];
727+
[self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths];
731728

732729
[_editingTransactionQueue addOperationWithBlock:^{
733730
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
@@ -777,7 +774,7 @@ - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDat
777774
}
778775

779776
// Measure nodes whose views are loaded before we leave the main thread
780-
[self _layoutNodesWithMainThreadAffinity:nodes atIndexPaths:indexPaths];
777+
[self layoutLoadedNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths];
781778

782779
[_editingTransactionQueue addOperationWithBlock:^{
783780
LOG(@"Edit Transaction - reloadRows: %@", indexPaths);

examples/ASCollectionView/Sample/ViewController.m

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ - (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplem
9292

9393
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
9494
{
95-
return 300;
95+
return 10;
96+
}
97+
98+
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
99+
{
100+
return 100;
96101
}
97102

98103
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView

0 commit comments

Comments
 (0)