Merge pull request #1220 from binl/bl_fix_changeset_datacontroller

[ASDataController] Improvements to index paths shifting reloads in changesets.
This commit is contained in:
appleguy
2016-02-12 00:06:24 -08:00
5 changed files with 48 additions and 25 deletions

View File

@@ -11,6 +11,8 @@
#import "_ASHierarchyChangeSet.h"
#import "ASAssert.h"
#import "ASDataController+Subclasses.h"
@interface ASChangeSetDataController ()
@property (nonatomic, assign) NSUInteger batchUpdateCounter;
@@ -51,15 +53,7 @@
[_changeSet markCompleted];
[super beginUpdates];
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) {
[super deleteRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
@@ -67,7 +61,15 @@
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) {
[super deleteSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadRowsAtIndexPaths:change.indexPaths withIndexPathsAfterUpdates:change.indexPathsAfterUpdates withAnimationOptions:change.animationOptions];
}
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) {
[super insertSections:change.indexSet withAnimationOptions:change.animationOptions];
}
@@ -75,7 +77,7 @@
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) {
[super insertRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
[super endUpdatesAnimated:animated completion:completion];
_changeSet = nil;

View File

@@ -50,6 +50,14 @@
*/
- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
#pragma mark - Row Editing (Internal API)
/**
* reload a set of IndexPaths requires deleting the cells at their current indexPaths
* and then inserting new ones at their future indexPaths, and these two sets of indexPath will be different in many cases
*/
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withIndexPathsAfterUpdates:(NSArray *)indexPathAfterUpdates withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
#pragma mark - Node & Section Insertion/Deletion API
/**

View File

@@ -787,6 +787,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self reloadRowsAtIndexPaths:indexPaths withIndexPathsAfterUpdates:indexPaths withAnimationOptions:animationOptions];
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withIndexPathsAfterUpdates:(NSArray *)indexPathAfterUpdates withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
@@ -796,20 +801,20 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
// Reloading requires re-fetching the data. Load it on the current calling thread, locking the data source.
[self accessDataSourceWithBlock:^{
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPathAfterUpdates.count];
// FIXME: This doesn't currently do anything
// FIXME: Shouldn't deletes be sorted in descending order?
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
for (NSIndexPath *indexPath in indexPaths) {
for (NSIndexPath *indexPath in indexPathAfterUpdates) {
[nodes addObject:[_dataSource dataController:self nodeBlockAtIndexPath:indexPath]];
}
[_editingTransactionQueue addOperationWithBlock:^{
LOG(@"Edit Transaction - reloadRows: %@", indexPaths);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
[self _batchLayoutNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
[self _batchLayoutNodes:nodes atIndexPaths:indexPathAfterUpdates withAnimationOptions:animationOptions];
}];
}];
}];

View File

@@ -30,7 +30,9 @@ typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) {
@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions;
/// Index paths are sorted descending for changeType .Delete, ascending otherwise
@property (nonatomic, strong) NSArray *indexPaths;
@property (nonatomic, strong, readonly) NSArray *indexPaths;
/// Calculated indexPaths after any insertions or deletions
@property (nonatomic, strong) NSArray *indexPathsAfterUpdates;
@property (nonatomic, readonly) _ASHierarchyChangeType changeType;
+ (NSDictionary *)sectionToIndexSetMapFromChanges:(NSArray *)changes ofType:(_ASHierarchyChangeType)changeType;

View File

@@ -198,9 +198,8 @@
// Ignore item inserts in reloaded(new)/inserted sections.
[_ASHierarchyItemChange sortAndCoalesceChanges:_insertItemChanges ignoringChangesInSections:insertedOrReloaded];
// reload itemsChanges need to be adjusted so that we access the correct indexPaths in the datasource
// reload items changes need to be adjusted so that we access the correct indexPaths in the datasource
NSDictionary *insertedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_insertItemChanges ofType:_ASHierarchyChangeTypeInsert];
NSDictionary *deletedIndexPathsMap = [_ASHierarchyItemChange sectionToIndexSetMapFromChanges:_deleteItemChanges ofType:_ASHierarchyChangeTypeDelete];
@@ -210,23 +209,30 @@
// Every indexPaths in the change need to update its section and/or row
// depending on all the deletions and insertions
// For reference, when batching reloads/deletes/inserts:
// - delete/reload indexPaths that are passed in should all be their current indexPaths
// - insert indexPaths that are passed in should all be their future indexPaths after deletions
for (NSIndexPath *indexPath in change.indexPaths) {
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
section -= [_deletedSections countOfIndexesInRange:NSMakeRange(0, section)];
section += [_insertedSections countOfIndexesInRange:NSMakeRange(0, section)];
NSIndexSet *indicesInsertedInSection = insertedIndexPathsMap[@(section).stringValue];
row += [indicesInsertedInSection countOfIndexesInRange:NSMakeRange(0, row)];
NSIndexSet *indicesDeletedInSection = deletedIndexPathsMap[@(section).stringValue];
row += [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, row)];
// Update section number based on section insertions/deletions that are above the current section
section -= [_deletedSections countOfIndexesInRange:NSMakeRange(0, section + 1)];
section += [_insertedSections countOfIndexesInRange:NSMakeRange(0, section + 1)];
// Update row number based on deletions that are above the current row in the current section
NSIndexSet *indicesDeletedInSection = deletedIndexPathsMap[@(indexPath.section)];
row -= [indicesDeletedInSection countOfIndexesInRange:NSMakeRange(0, row + 1)];
// Update row number based on insertions that are above the current row in the future section
NSIndexSet *indicesInsertedInSection = insertedIndexPathsMap[@(section)];
row += [indicesInsertedInSection countOfIndexesInRange:NSMakeRange(0, row + 1)];
//TODO: reuse the old indexPath object if section and row aren't changed
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:row inSection:section];
[newIndexPaths addObject:newIndexPath];
}
change.indexPaths = newIndexPaths;
change.indexPathsAfterUpdates = newIndexPaths;
}
}
}
@@ -342,7 +348,7 @@
for (_ASHierarchyItemChange *change in changes) {
NSAssert(change.changeType == changeType, @"The map we created must all be of the same changeType as of now");
for (NSIndexPath *indexPath in change.indexPaths) {
NSString *sectionKey = @(indexPath.section).stringValue;
NSString *sectionKey = @(indexPath.section);
NSMutableIndexSet *indexSet = sectionToIndexSetMap[sectionKey];
if (indexSet) {
[indexSet addIndex:indexPath.row];