[ASDataController] If app code issues edit commands before first reload, ignore them, as UIKit will call -reloadData.

This commit is contained in:
Scott Goodson 2016-03-05 20:22:18 -08:00
parent 354ccf3e5f
commit cc7ca4a08f
4 changed files with 66 additions and 47 deletions

View File

@ -42,6 +42,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes. NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes.
BOOL _asyncDataFetchingEnabled; BOOL _asyncDataFetchingEnabled;
BOOL _initialReloadDataHasBeenCalled;
BOOL _delegateDidInsertNodes; BOOL _delegateDidInsertNodes;
BOOL _delegateDidDeleteNodes; BOOL _delegateDidDeleteNodes;
@ -267,7 +269,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
return; return;
} }
LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind])); LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForTwoDimensionalArray(_editingNodes[kind]));
NSMutableArray *editingNodes = _editingNodes[kind]; NSMutableArray *editingNodes = _editingNodes[kind];
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths); ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
_editingNodes[kind] = editingNodes; _editingNodes[kind] = editingNodes;
@ -376,32 +378,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
#pragma mark - Initial Load & Full Reload (External API) #pragma mark - Initial Load & Full Reload (External API)
- (void)initialDataLoadingWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[self accessDataSourceWithBlock:^{
NSMutableArray *indexPaths = [NSMutableArray array];
NSUInteger sectionNum = [_dataSource numberOfSectionsInDataController:self];
// insert sections
[self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOptions:0];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
[indexPaths addObject:[indexPath indexPathByAddingIndex:j]];
}
}
// insert elements
[self insertRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
}];
}];
}
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion - (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion
{ {
[self _reloadDataWithAnimationOptions:animationOptions synchronously:NO completion:completion]; [self _reloadDataWithAnimationOptions:animationOptions synchronously:NO completion:completion];
@ -414,6 +390,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions synchronously:(BOOL)synchronously completion:(void (^)())completion - (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions synchronously:(BOOL)synchronously completion:(void (^)())completion
{ {
_initialReloadDataHasBeenCalled = YES;
[self performEditCommandWithBlock:^{ [self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
[_editingTransactionQueue waitUntilAllOperationsAreFinished]; [_editingTransactionQueue waitUntilAllOperationsAreFinished];
@ -430,12 +407,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
LOG(@"Edit Transaction - reloadData"); LOG(@"Edit Transaction - reloadData");
// Remove everything that existed before the reload, now that we're ready to insert replacements // Remove everything that existed before the reload, now that we're ready to insert replacements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind]);
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind]; NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind];
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; NSUInteger editingNodesSectionCount = editingNodes.count;
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
if (editingNodesSectionCount) {
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodesSectionCount)];
[self _deleteNodesAtIndexPaths:ASIndexPathsForTwoDimensionalArray(editingNodes) withAnimationOptions:animationOptions];
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
}
[self willReloadData]; [self willReloadData];
@ -591,6 +570,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
{ {
// This method needs to block the thread and synchronously perform the operation if we are not // This method needs to block the thread and synchronously perform the operation if we are not
// queuing commands for begin/endUpdates. If we are queuing, it needs to return immediately. // queuing commands for begin/endUpdates. If we are queuing, it needs to return immediately.
if (!_initialReloadDataHasBeenCalled) {
return;
}
// If we have never performed a reload, there is no value in executing edit operations as the initial
// reload will directly re-query the latest state of the datasource - so completely skip the block in this case.
if (_batchUpdateCounter == 0) { if (_batchUpdateCounter == 0) {
block(); block();
} else { } else {
@ -667,7 +652,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections); NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections);
LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASDataControllerRowNodeKind])); LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForTwoDimensionalArray(_editingNodes[ASDataControllerRowNodeKind]));
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions]; [self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
@ -900,7 +885,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind - (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind
{ {
return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : nil; return _editingNodes[kind] != nil ? ASIndexPathsForTwoDimensionalArray(_editingNodes[kind]) : nil;
} }
- (NSMutableArray *)editingNodesOfKind:(NSString *)kind - (NSMutableArray *)editingNodesOfKind:(NSString *)kind

View File

@ -65,4 +65,9 @@
} }
} }
- (NSString *)description
{
return [[super description] stringByAppendingFormat:@" Blocks: %@", _blocks];
}
@end @end

View File

@ -50,7 +50,12 @@ extern NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray
extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *MultidimensionalArray, NSIndexSet *indexSet); extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *MultidimensionalArray, NSIndexSet *indexSet);
/** /**
* Return all the index paths of mutable multidimensional array, in ascending order. * Return all the index paths of a two-dimensional array, in ascending order.
*/
extern NSArray *ASIndexPathsForTwoDimensionalArray(NSArray <NSArray *>* twoDimensionalArray);
/**
* Return all the index paths of a multidimensional array, in ascending order.
*/ */
extern NSArray *ASIndexPathsForMultidimensionalArray(NSArray *MultidimensionalArray); extern NSArray *ASIndexPathsForMultidimensionalArray(NSArray *MultidimensionalArray);

View File

@ -16,7 +16,8 @@ static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray
NSUInteger &curIdx, NSUInteger &curIdx,
NSIndexPath *curIndexPath, NSIndexPath *curIndexPath,
const NSUInteger dimension, const NSUInteger dimension,
void (^updateBlock)(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx)) { void (^updateBlock)(NSMutableArray *arr, NSIndexSet *indexSet, NSUInteger idx))
{
if (curIdx == indexPaths.count) { if (curIdx == indexPaths.count) {
return; return;
} }
@ -38,7 +39,8 @@ static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray
} }
} }
static void ASRecursivelyFindIndexPathsForMultidimensionalArray(NSObject *obj, NSIndexPath *curIndexPath, NSMutableArray *res) { static void ASRecursivelyFindIndexPathsForMultidimensionalArray(NSObject *obj, NSIndexPath *curIndexPath, NSMutableArray *res)
{
if (![obj isKindOfClass:[NSArray class]]) { if (![obj isKindOfClass:[NSArray class]]) {
[res addObject:curIndexPath]; [res addObject:curIndexPath];
} else { } else {
@ -51,7 +53,8 @@ static void ASRecursivelyFindIndexPathsForMultidimensionalArray(NSObject *obj, N
#pragma mark - Public Methods #pragma mark - Public Methods
NSObject<NSCopying> *ASMultidimensionalArrayDeepMutableCopy(NSObject<NSCopying> *obj) { NSObject<NSCopying> *ASMultidimensionalArrayDeepMutableCopy(NSObject<NSCopying> *obj)
{
if ([obj isKindOfClass:[NSArray class]]) { if ([obj isKindOfClass:[NSArray class]]) {
NSArray *arr = (NSArray *)obj; NSArray *arr = (NSArray *)obj;
NSMutableArray * mutableArr = [NSMutableArray arrayWithCapacity:arr.count]; NSMutableArray * mutableArr = [NSMutableArray arrayWithCapacity:arr.count];
@ -64,15 +67,18 @@ NSObject<NSCopying> *ASMultidimensionalArrayDeepMutableCopy(NSObject<NSCopying>
return obj; return obj;
} }
NSMutableArray<NSMutableArray *> *ASTwoDimensionalArrayDeepMutableCopy(NSArray<NSArray *> *array) { NSMutableArray<NSMutableArray *> *ASTwoDimensionalArrayDeepMutableCopy(NSArray<NSArray *> *array)
{
NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count]; NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
for (NSArray *subarray in array) { for (NSArray *subarray in array) {
ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray<NSArray *> *");
[newArray addObject:[subarray mutableCopy]]; [newArray addObject:[subarray mutableCopy]];
} }
return newArray; return newArray;
} }
void ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths, NSArray *elements) { void ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths, NSArray *elements)
{
ASDisplayNodeCAssert(indexPaths.count == elements.count, @"Inconsistent indexPaths and elements"); ASDisplayNodeCAssert(indexPaths.count == elements.count, @"Inconsistent indexPaths and elements");
if (!indexPaths.count) { if (!indexPaths.count) {
@ -89,7 +95,8 @@ void ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(NSMutableArray *mutab
ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !"); ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !");
} }
void ASDeleteElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) { void ASDeleteElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths)
{
if (!indexPaths.count) { if (!indexPaths.count) {
return; return;
} }
@ -104,7 +111,8 @@ void ASDeleteElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutable
ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !"); ASDisplayNodeCAssert(curIdx == indexPaths.count, @"Indexpath is out of range !");
} }
NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths) { NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray, NSArray *indexPaths)
{
NSUInteger curIdx = 0; NSUInteger curIdx = 0;
NSIndexPath *indexPath = [[NSIndexPath alloc] init]; NSIndexPath *indexPath = [[NSIndexPath alloc] init];
NSMutableArray *deletedElements = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; NSMutableArray *deletedElements = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
@ -122,8 +130,9 @@ NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray *mutab
return deletedElements; return deletedElements;
} }
NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensionalArray, NSIndexSet *indexSet) { NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensionalArray, NSIndexSet *indexSet)
NSMutableArray *res = [[NSMutableArray alloc] initWithCapacity:multidimensionalArray.count]; {
NSMutableArray *res = [NSMutableArray array];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray[idx], [NSIndexPath indexPathWithIndex:idx], res); ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray[idx], [NSIndexPath indexPathWithIndex:idx], res);
}]; }];
@ -131,9 +140,24 @@ NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensiona
return res; return res;
} }
NSArray *ASIndexPathsForMultidimensionalArray(NSArray *multidimensionalArray) { NSArray *ASIndexPathsForTwoDimensionalArray(NSArray <NSArray *>* twoDimensionalArray)
NSMutableArray *res = [NSMutableArray arrayWithCapacity:multidimensionalArray.count]; {
NSMutableArray *result = [NSMutableArray array];
NSUInteger section = 0;
for (NSArray *subarray in twoDimensionalArray) {
ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray<NSArray *> *");
NSUInteger itemCount = subarray.count;
for (int item = 0; item < itemCount; item++) {
[result addObject:[NSIndexPath indexPathForItem:item inSection:section]];
}
section++;
}
return result;
}
NSArray *ASIndexPathsForMultidimensionalArray(NSArray *multidimensionalArray)
{
NSMutableArray *res = [NSMutableArray array];
ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray, [[NSIndexPath alloc] init], res); ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray, [[NSIndexPath alloc] init], res);
return res; return res;
} }