mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-31 01:42:18 +00:00
Merge pull request #523 from facebook/DataControllerSerialization
Clean up organization and naming of methods internal to ASDataController.
This commit is contained in:
commit
1563c5e577
@ -127,7 +127,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)performDataFetchingWithBlock:(dispatch_block_t)block {
|
- (void)accessDataSourceWithBlock:(dispatch_block_t)block {
|
||||||
if (_asyncDataFetchingEnabled) {
|
if (_asyncDataFetchingEnabled) {
|
||||||
[_dataSource dataControllerLockDataSource];
|
[_dataSource dataControllerLockDataSource];
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||||
@ -141,10 +141,119 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Initial Data Loading
|
#pragma mark - Cell Layout
|
||||||
|
|
||||||
|
- (void)_layoutNodes:(NSArray *)nodes
|
||||||
|
atIndexPaths:(NSArray *)indexPaths
|
||||||
|
withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
if (!nodes.count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_group_t layoutGroup = dispatch_group_create();
|
||||||
|
|
||||||
|
for (NSUInteger j = 0; j < nodes.count && j < indexPaths.count; j += kASDataControllerSizingCountPerProcessor) {
|
||||||
|
NSArray *subIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(j, MIN(kASDataControllerSizingCountPerProcessor, indexPaths.count - j))];
|
||||||
|
|
||||||
|
// TODO: The current implementation does not make use of different constrained sizes per node.
|
||||||
|
// There should be a fast-path that avoids all of this object creation.
|
||||||
|
NSMutableArray *nodeBoundSizes = [[NSMutableArray alloc] initWithCapacity:kASDataControllerSizingCountPerProcessor];
|
||||||
|
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||||
|
[nodeBoundSizes addObject:[NSValue valueWithCGSize:[_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]]];
|
||||||
|
}];
|
||||||
|
|
||||||
|
dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
|
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||||
|
ASCellNode *node = nodes[j + idx];
|
||||||
|
[node measure:[nodeBoundSizes[idx] CGSizeValue]];
|
||||||
|
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_block_t block = ^{
|
||||||
|
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
|
||||||
|
|
||||||
|
[self asyncUpdateDataWithBlock:^{
|
||||||
|
// Insert finished nodes into data storage
|
||||||
|
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
if ([ASDataController executingOnSizingQueue]) {
|
||||||
|
block();
|
||||||
|
} else {
|
||||||
|
dispatch_async([ASDataController sizingQueue], block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_batchLayoutNodes:(NSArray *)nodes
|
||||||
|
atIndexPaths:(NSArray *)indexPaths
|
||||||
|
withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
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 withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Internal Data Editing
|
||||||
|
|
||||||
|
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
if (indexPaths.count == 0)
|
||||||
|
return;
|
||||||
|
if (_delegateWillInsertNodes)
|
||||||
|
[_delegate dataController:self willInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodes, indexPaths, nodes);
|
||||||
|
if (_delegateDidInsertNodes)
|
||||||
|
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
if (indexPaths.count == 0)
|
||||||
|
return;
|
||||||
|
if (_delegateWillDeleteNodes)
|
||||||
|
[_delegate dataController:self willDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths);
|
||||||
|
if (_delegateDidDeleteNodes)
|
||||||
|
[_delegate dataController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_insertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
if (indexSet.count == 0)
|
||||||
|
return;
|
||||||
|
if (_delegateWillInsertSections)
|
||||||
|
[_delegate dataController:self willInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
|
[_nodes insertObjects:sections atIndexes:indexSet];
|
||||||
|
if (_delegateDidInsertSections)
|
||||||
|
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
|
{
|
||||||
|
if (indexSet.count == 0)
|
||||||
|
return;
|
||||||
|
if (_delegateWillDeleteSections)
|
||||||
|
[_delegate dataController:self willDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
|
[_nodes removeObjectsAtIndexes:indexSet];
|
||||||
|
if (_delegateDidDeleteSections)
|
||||||
|
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Initial Load & Full Reload (External API)
|
||||||
|
|
||||||
- (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
- (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions {
|
||||||
[self performDataFetchingWithBlock:^{
|
[self accessDataSourceWithBlock:^{
|
||||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||||
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
||||||
|
|
||||||
@ -166,7 +275,53 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Data Update
|
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion
|
||||||
|
{
|
||||||
|
[self accessDataSourceWithBlock:^{
|
||||||
|
// Fetching data in calling thread
|
||||||
|
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||||
|
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
||||||
|
for (NSUInteger i = 0; i < sectionNum; i++) {
|
||||||
|
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
|
||||||
|
|
||||||
|
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
|
||||||
|
for (NSUInteger j = 0; j < rowNum; j++) {
|
||||||
|
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
|
||||||
|
[updatedIndexPaths addObject:indexPath];
|
||||||
|
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch_async([ASDataController sizingQueue], ^{
|
||||||
|
[self syncUpdateDataWithBlock:^{
|
||||||
|
// Remove everything that existed before the reload, now that we're ready to insert replacements
|
||||||
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes);
|
||||||
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
|
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)];
|
||||||
|
[self deleteSections:indexSet withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
|
// Insert each section
|
||||||
|
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:sectionNum];
|
||||||
|
for (int i = 0; i < sectionNum; i++) {
|
||||||
|
[sections addObject:[[NSMutableArray alloc] init]];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self _insertSections:sections atIndexSet:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:animationOptions];
|
||||||
|
}];
|
||||||
|
|
||||||
|
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
|
if (completion) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), completion);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Batching (External API)
|
||||||
|
|
||||||
- (void)beginUpdates
|
- (void)beginUpdates
|
||||||
{
|
{
|
||||||
@ -199,9 +354,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Section Editing (External API)
|
||||||
|
|
||||||
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
[self performDataFetchingWithBlock:^{
|
[self accessDataSourceWithBlock:^{
|
||||||
__block int nodeTotalCnt = 0;
|
__block int nodeTotalCnt = 0;
|
||||||
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
|
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
|
||||||
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||||
@ -235,7 +392,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[self _insertSections:sectionArray atIndexSet:indexSet withAnimationOptions:animationOptions];
|
[self _insertSections:sectionArray atIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -247,7 +404,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
// remove elements
|
// remove elements
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, indexSet);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, indexSet);
|
||||||
|
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
@ -255,7 +412,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
[self performDataFetchingWithBlock:^{
|
[self accessDataSourceWithBlock:^{
|
||||||
// We need to keep data query on data source in the calling thread.
|
// We need to keep data query on data source in the calling thread.
|
||||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
||||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
||||||
@ -278,11 +435,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[self syncUpdateDataWithBlock:^{
|
[self syncUpdateDataWithBlock:^{
|
||||||
// remove elements
|
// remove elements
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections);
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// reinsert the elements
|
// reinsert the elements
|
||||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
|
[self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -294,7 +451,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
// remove elements
|
// remove elements
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, [NSIndexSet indexSetWithIndex:section]);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, [NSIndexSet indexSetWithIndex:section]);
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths);
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths);
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
// update the section of indexpaths
|
// update the section of indexpaths
|
||||||
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection];
|
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection];
|
||||||
@ -304,75 +461,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
// Don't re-calculate size for moving
|
// Don't re-calculate size for moving
|
||||||
[self _insertNodes:nodes atIndexPaths:updatedIndexPaths animationOptions:animationOptions];
|
[self _insertNodes:nodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_insertNodes:(NSArray *)nodes
|
#pragma mark - Row Editing (External API)
|
||||||
atIndexPaths:(NSArray *)indexPaths
|
|
||||||
withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
if (!nodes.count) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_group_t layoutGroup = dispatch_group_create();
|
|
||||||
|
|
||||||
for (NSUInteger j = 0; j < nodes.count && j < indexPaths.count; j += kASDataControllerSizingCountPerProcessor) {
|
|
||||||
NSArray *subIndexPaths = [indexPaths subarrayWithRange:NSMakeRange(j, MIN(kASDataControllerSizingCountPerProcessor, indexPaths.count - j))];
|
|
||||||
|
|
||||||
// TODO: The current implementation does not make use of different constrained sizes per node.
|
|
||||||
// There should be a fast-path that avoids all of this object creation.
|
|
||||||
NSMutableArray *nodeBoundSizes = [[NSMutableArray alloc] initWithCapacity:kASDataControllerSizingCountPerProcessor];
|
|
||||||
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
|
||||||
[nodeBoundSizes addObject:[NSValue valueWithCGSize:[_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]]];
|
|
||||||
}];
|
|
||||||
|
|
||||||
dispatch_group_async(layoutGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
||||||
[subIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
|
||||||
ASCellNode *node = nodes[j + idx];
|
|
||||||
[node measure:[nodeBoundSizes[idx] CGSizeValue]];
|
|
||||||
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_block_t block = ^{
|
|
||||||
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
|
|
||||||
|
|
||||||
[self asyncUpdateDataWithBlock:^{
|
|
||||||
// Insert finished nodes into data storage
|
|
||||||
[self _insertNodes:nodes atIndexPaths:indexPaths animationOptions:animationOptions];
|
|
||||||
}];
|
|
||||||
};
|
|
||||||
|
|
||||||
if ([ASDataController executingOnSizingQueue]) {
|
|
||||||
block();
|
|
||||||
} else {
|
|
||||||
dispatch_async([ASDataController sizingQueue], block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_batchInsertNodes:(NSArray *)nodes
|
|
||||||
atIndexPaths:(NSArray *)indexPaths
|
|
||||||
withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
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 _insertNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
[self performDataFetchingWithBlock:^{
|
[self accessDataSourceWithBlock:^{
|
||||||
// sort indexPath to avoid messing up the index when inserting in several batches
|
// sort indexPath to avoid messing up the index when inserting in several batches
|
||||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
@ -380,7 +478,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
|
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +489,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
dispatch_async([ASDataController sizingQueue], ^{
|
dispatch_async([ASDataController sizingQueue], ^{
|
||||||
[self asyncUpdateDataWithBlock:^{
|
[self asyncUpdateDataWithBlock:^{
|
||||||
[self _deleteNodesAtIndexPaths:sortedIndexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -399,7 +497,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
// Reloading requires re-fetching the data. Load it on the current calling thread, locking the data source.
|
// Reloading requires re-fetching the data. Load it on the current calling thread, locking the data source.
|
||||||
[self performDataFetchingWithBlock:^{
|
[self accessDataSourceWithBlock:^{
|
||||||
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||||
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
|
||||||
@ -408,10 +506,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
dispatch_async([ASDataController sizingQueue], ^{
|
dispatch_async([ASDataController sizingQueue], ^{
|
||||||
[self syncUpdateDataWithBlock:^{
|
[self syncUpdateDataWithBlock:^{
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@ -422,62 +520,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[self asyncUpdateDataWithBlock:^{
|
[self asyncUpdateDataWithBlock:^{
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [NSArray arrayWithObject:indexPath]);
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [NSArray arrayWithObject:indexPath]);
|
||||||
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
|
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
// Don't re-calculate size for moving
|
// Don't re-calculate size for moving
|
||||||
NSArray *newIndexPaths = [NSArray arrayWithObject:newIndexPath];
|
NSArray *newIndexPaths = [NSArray arrayWithObject:newIndexPath];
|
||||||
[self _insertNodes:nodes atIndexPaths:newIndexPaths animationOptions:animationOptions];
|
[self _insertNodes:nodes atIndexPaths:newIndexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion
|
#pragma mark - Data Querying (External API)
|
||||||
{
|
|
||||||
[self performDataFetchingWithBlock:^{
|
|
||||||
// Fetching data in calling thread
|
|
||||||
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
|
|
||||||
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
|
|
||||||
|
|
||||||
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
|
|
||||||
for (NSUInteger i = 0; i < sectionNum; i++) {
|
|
||||||
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
|
|
||||||
|
|
||||||
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
|
|
||||||
for (NSUInteger j = 0; j < rowNum; j++) {
|
|
||||||
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
|
|
||||||
[updatedIndexPaths addObject:indexPath];
|
|
||||||
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch_async([ASDataController sizingQueue], ^{
|
|
||||||
[self syncUpdateDataWithBlock:^{
|
|
||||||
// Remove everything that existed before the reload, now that we're ready to insert replacements
|
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes);
|
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths animationOptions:animationOptions];
|
|
||||||
|
|
||||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)];
|
|
||||||
[self deleteSections:indexSet withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
// Insert each section
|
|
||||||
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:sectionNum];
|
|
||||||
for (int i = 0; i < sectionNum; i++) {
|
|
||||||
[sections addObject:[[NSMutableArray alloc] init]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _insertSections:sections atIndexSet:[[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:animationOptions];
|
|
||||||
}];
|
|
||||||
|
|
||||||
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
if (completion) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), completion);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Data Querying
|
|
||||||
|
|
||||||
- (NSUInteger)numberOfSections
|
- (NSUInteger)numberOfSections
|
||||||
{
|
{
|
||||||
@ -500,7 +552,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
- (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths
|
- (NSArray *)nodesAtIndexPaths:(NSArray *)indexPaths
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
// Make sure that any asynchronous layout operations have finished so that those nodes are present.
|
// Make sure that any asynchronous layout operations have finished so that those nodes are present.
|
||||||
// Otherwise a failure case could be:
|
// Otherwise a failure case could be:
|
||||||
// - Reload section 2, deleting all current nodes in that section.
|
// - Reload section 2, deleting all current nodes in that section.
|
||||||
@ -512,52 +564,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
return ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]);
|
return ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [indexPaths sortedArrayUsingSelector:@selector(compare:)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Internal Data Updating
|
|
||||||
|
|
||||||
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
if (indexPaths.count == 0)
|
|
||||||
return;
|
|
||||||
if (_delegateWillInsertNodes)
|
|
||||||
[_delegate dataController:self willInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
|
||||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodes, indexPaths, nodes);
|
|
||||||
if (_delegateDidInsertNodes)
|
|
||||||
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
if (indexPaths.count == 0)
|
|
||||||
return;
|
|
||||||
if (_delegateWillDeleteNodes)
|
|
||||||
[_delegate dataController:self willDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
|
||||||
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths);
|
|
||||||
if (_delegateDidDeleteNodes)
|
|
||||||
[_delegate dataController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_insertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
if (indexSet.count == 0)
|
|
||||||
return;
|
|
||||||
if (_delegateWillInsertSections)
|
|
||||||
[_delegate dataController:self willInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
|
||||||
[_nodes insertObjects:sections atIndexes:indexSet];
|
|
||||||
if (_delegateDidInsertSections)
|
|
||||||
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
|
||||||
{
|
|
||||||
if (indexSet.count == 0)
|
|
||||||
return;
|
|
||||||
if (_delegateWillDeleteSections)
|
|
||||||
[_delegate dataController:self willDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
|
||||||
[_nodes removeObjectsAtIndexes:indexSet];
|
|
||||||
if (_delegateDidDeleteSections)
|
|
||||||
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Dealloc
|
#pragma mark - Dealloc
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user