diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 09d2a6b9c0..167ba8f991 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -213,7 +213,7 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)beginUpdates { - [self throwUnimplementedException]; + [_dataController beginUpdates]; } - (void)endUpdates @@ -223,17 +223,17 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController insertSections:sections]; + [_dataController insertSections:sections withAnimationOption:animation]; } - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController deleteSections:sections]; + [_dataController deleteSections:sections withAnimationOption:animation]; } - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController reloadSections:sections]; + [_dataController reloadSections:sections withAnimationOption:animation]; } - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection @@ -243,17 +243,17 @@ static BOOL _isInterceptedSelector(SEL sel) - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController insertRowsAtIndexPaths:indexPaths]; + [_dataController insertRowsAtIndexPaths:indexPaths withAnimationOption:animation]; } - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController deleteRowsAtIndexPaths:indexPaths]; + [_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOption:animation]; } - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation { - [_dataController reloadRowsAtIndexPaths:indexPaths]; + [_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOption:animation]; } - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath @@ -341,6 +341,16 @@ static BOOL _isInterceptedSelector(SEL sel) #pragma mark - #pragma mark ASRangeControllerDelegate. +- (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController { + ASDisplayNodeAssertMainThread(); + [super beginUpdates]; +} + +- (void)rangeControllerEndUpdates:(ASRangeController *)rangeController { + ASDisplayNodeAssertMainThread(); + [super endUpdates]; +} + - (NSArray *)rangeControllerVisibleNodeIndexPaths:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); @@ -358,40 +368,40 @@ static BOOL _isInterceptedSelector(SEL sel) return self.bounds.size; } -- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths +- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssertMainThread(); - [UIView performWithoutAnimation:^{ - [super insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; - }]; +// [UIView performWithoutAnimation:^{ + [super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOption]; +// }]; } -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths +- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssertMainThread(); - [UIView performWithoutAnimation:^{ - [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; - }]; +// [UIView performWithoutAnimation:^{ + [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOption]; +// }]; } -- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet +- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssertMainThread(); - [UIView performWithoutAnimation:^{ - [super insertSections:indexSet withRowAnimation:UITableViewRowAnimationNone]; - }]; +// [UIView performWithoutAnimation:^{ + [super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOption]; +// }]; } -- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet +- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssertMainThread(); - [UIView performWithoutAnimation:^{ - [super deleteSections:indexSet withRowAnimation:UITableViewRowAnimationNone]; - }]; +// [UIView performWithoutAnimation:^{ + [super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOption]; +// }]; } #pragma mark - ASDataControllerDelegate diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index c1f59561fd..b75ab7e4ef 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -6,6 +6,8 @@ @class ASCellNode; @class ASDataController; +typedef NSUInteger ASDataControllerAnimationOptions; + /** Data source for data controller It will be invoked in the same thread as the api call of ASDataController. @@ -42,29 +44,35 @@ @optional +/** + Called for batch update. + */ +- (void)dataControllerBeginUpdates:(ASDataController *)dataController; +- (void)dataControllerEndUpdates:(ASDataController *)dataController; + /** Called for insertion of elements. */ -- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths; -- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths; +- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; +- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** Called for deletion of elements. */ -- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths; -- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths; +- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; +- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** Called for insertion of sections. */ -- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet; -- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet; +- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; +- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** Called for deletion of sections. */ -- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet; -- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet; +- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; +- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; @end @@ -94,19 +102,23 @@ /** @name Data Updating */ -- (void)insertSections:(NSIndexSet *)sections; +- (void)beginUpdates; -- (void)deleteSections:(NSIndexSet *)sections; +- (void)endUpdates; -- (void)reloadSections:(NSIndexSet *)sections; +- (void)insertSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption; + +- (void)deleteSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption;; + +- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption; - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection; -- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths; +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; -- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths; +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; -- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths; +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 1a394685b5..7f70f2475c 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -8,48 +8,49 @@ #import "ASCellNode.h" #import "ASDisplayNode.h" #import "ASMultidimensionalArrayUtils.h" +#import "ASDisplayNodeInternal.h" -#define INSERT_NODES(multidimensionalArray, indexPath, elements) \ +#define INSERT_NODES(multidimensionalArray, indexPath, elements, animationOption) \ { \ - if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:)]) { \ - [_delegate dataController:self willInsertNodes:elements atIndexPaths:indexPath]; \ + if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \ + [_delegate dataController:self willInsertNodes:elements atIndexPaths:indexPath withAnimationOption:animationOption]; \ } \ ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(multidimensionalArray, indexPath, elements); \ - if ([_delegate respondsToSelector:@selector(dataController:didInsertNodes:atIndexPaths:)]) { \ - [_delegate dataController:self didInsertNodes:elements atIndexPaths:indexPath]; \ + if ([_delegate respondsToSelector:@selector(dataController:didInsertNodes:atIndexPaths:withAnimationOption:)]) { \ + [_delegate dataController:self didInsertNodes:elements atIndexPaths:indexPath withAnimationOption:animationOption]; \ } \ } -#define DELETE_NODES(multidimensionalArray, indexPath) \ +#define DELETE_NODES(multidimensionalArray, indexPath, animationOption) \ { \ - if ([_delegate respondsToSelector:@selector(dataController:willDeleteNodesAtIndexPaths:)]) { \ - [_delegate dataController:self willDeleteNodesAtIndexPaths:indexPath]; \ + if ([_delegate respondsToSelector:@selector(dataController:willDeleteNodesAtIndexPaths:withAnimationOption:)]) { \ + [_delegate dataController:self willDeleteNodesAtIndexPaths:indexPath withAnimationOption:animationOption]; \ } \ ASDeleteElementsInMultidimensionalArrayAtIndexPaths(multidimensionalArray, indexPath); \ - if ([_delegate respondsToSelector:@selector(dataController:didDeleteNodesAtIndexPaths:)]) { \ - [_delegate dataController:self didDeleteNodesAtIndexPaths:indexPath]; \ + if ([_delegate respondsToSelector:@selector(dataController:didDeleteNodesAtIndexPaths:withAnimationOption:)]) { \ + [_delegate dataController:self didDeleteNodesAtIndexPaths:indexPath withAnimationOption:animationOption]; \ } \ } -#define INSERT_SECTIONS(multidimensionalArray, indexSet, sections) \ +#define INSERT_SECTIONS(multidimensionalArray, indexSet, sections, animationOption) \ { \ - if ([_delegate respondsToSelector:@selector(dataController:willInsertSections:atIndexSet:)]) { \ - [_delegate dataController:self willInsertSections:sections atIndexSet:indexSet]; \ + if ([_delegate respondsToSelector:@selector(dataController:willInsertSections:atIndexSet:withAnimationOption:)]) { \ + [_delegate dataController:self willInsertSections:sections atIndexSet:indexSet withAnimationOption:animationOption]; \ } \ [multidimensionalArray insertObjects:sections atIndexes:indexSet]; \ - if ([_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:)]) { \ - [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet]; \ + if ([_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:withAnimationOption:)]) { \ + [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOption:animationOption]; \ } \ } -#define DELETE_SECTIONS(multidimensionalArray, indexSet) \ +#define DELETE_SECTIONS(multidimensionalArray, indexSet, animationOption) \ { \ - if ([_delegate respondsToSelector:@selector(dataController:willDeleteSectionsAtIndexSet:)]) { \ - [_delegate dataController:self willDeleteSectionsAtIndexSet:indexSet]; \ + if ([_delegate respondsToSelector:@selector(dataController:willDeleteSectionsAtIndexSet:withAnimationOption:)]) { \ + [_delegate dataController:self willDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; \ } \ [multidimensionalArray removeObjectsAtIndexes:indexSet]; \ - if ([_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:)]) { \ - [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet]; \ + if ([_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:withAnimationOption:)]) { \ + [_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; \ } \ } @@ -70,6 +71,7 @@ #define ENABLE_BACKGROUND_UPDATE 0 const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; +const static NSUInteger kASDataControllerAnimationOptionNone = 0; static void *kASSizingQueueContext = &kASSizingQueueContext; static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; @@ -78,6 +80,8 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; NSMutableArray *_nodes; } +@property (atomic, assign) NSUInteger batchUpdateCounter; + @end @implementation ASDataController @@ -85,6 +89,7 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; - (instancetype)init { if (self = [super init]) { _nodes = [NSMutableArray array]; + _batchUpdateCounter = 0; } return self; @@ -187,7 +192,7 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self]; // insert sections - [self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)]]; + [self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOption:0]; for (NSUInteger i = 0; i < sectionNum; i++) { NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:i]; @@ -199,12 +204,30 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; } // insert elements - [self insertRowsAtIndexPaths:indexPaths]; + [self insertRowsAtIndexPaths:indexPaths withAnimationOption:kASDataControllerAnimationOptionNone]; } #pragma mark - Data Update -- (void)insertSections:(NSIndexSet *)indexSet { +- (void)beginUpdates { + dispatch_async([[self class] sizingQueue], ^{ + [self asyncUpdateDataWithBlock:^{ + _batchUpdateCounter++; + [_delegate dataControllerBeginUpdates:self]; + }]; + }); +} + +- (void)endUpdates { + dispatch_async([[self class] sizingQueue], ^{ + [self asyncUpdateDataWithBlock:^{ + _batchUpdateCounter--; + [_delegate dataControllerEndUpdates:self]; + }]; + }); +} + +- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { __block int nodeTotalCnt = 0; NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count]; [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { @@ -235,26 +258,26 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; for (NSUInteger i = 0; i < indexSet.count; i++) { [sectionArray addObject:[NSMutableArray array]]; } - INSERT_SECTIONS(_nodes , indexSet, sectionArray); + INSERT_SECTIONS(_nodes , indexSet, sectionArray, animationOption); }]; - [self _batchInsertNodes:nodes atIndexPaths:indexPaths]; + [self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption]; }); } -- (void)deleteSections:(NSIndexSet *)indexSet { +- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { dispatch_async([[self class] sizingQueue], ^{ [self asyncUpdateDataWithBlock:^{ // remove elements NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, indexSet); - DELETE_NODES(_nodes, indexPaths); - DELETE_SECTIONS(_nodes, indexSet); + DELETE_NODES(_nodes, indexPaths, animationOption); + DELETE_SECTIONS(_nodes, indexSet, animationOption); }]; }); } -- (void)reloadSections:(NSIndexSet *)sections { +- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption { // We need to keep data query on data source in the calling thread. NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init]; NSMutableArray *updatedNodes = [[NSMutableArray alloc] init]; @@ -274,11 +297,11 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [self syncUpdateDataWithBlock:^{ // remove elements NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections); - DELETE_NODES(_nodes, indexPaths); + DELETE_NODES(_nodes, indexPaths, animationOption); }]; // reinsert the elements - [self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths]; + [self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption]; }); } @@ -288,7 +311,7 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; // remove elements NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, [NSIndexSet indexSetWithIndex:section]); NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, indexPaths); - DELETE_NODES(_nodes, indexPaths); + DELETE_NODES(_nodes, indexPaths, kASDataControllerAnimationOptionNone); // update the section of indexpaths NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:newSection]; @@ -298,13 +321,14 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; }]; // Don't re-calculate size for moving - INSERT_NODES(_nodes, updatedIndexPaths, nodes); + INSERT_NODES(_nodes, updatedIndexPaths, nodes, kASDataControllerAnimationOptionNone); }]; }); } - (void)_insertNodes:(NSArray *)nodes - atIndexPaths:(NSArray *)indexPaths { + atIndexPaths:(NSArray *)indexPaths + withAnimationOption:(ASDataControllerAnimationOptions)animationOption { if (!nodes.count) { return; } @@ -333,7 +357,7 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [self asyncUpdateDataWithBlock:^{ // updating the cells - INSERT_NODES(_nodes, indexPaths, nodes); + INSERT_NODES(_nodes, indexPaths, nodes, animationOption); }]; }; @@ -345,7 +369,8 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; } - (void)_batchInsertNodes:(NSArray *)nodes - atIndexPaths:(NSArray *)indexPaths { + atIndexPaths:(NSArray *)indexPaths + withAnimationOptions:(ASDataControllerAnimationOptions)animationOption { NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; // Processing in batches @@ -354,11 +379,11 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange]; NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange]; - [self _insertNodes:batchedNodes atIndexPaths:batchedIndexPaths]; + [self _insertNodes:batchedNodes atIndexPaths:batchedIndexPaths withAnimationOption:animationOption]; } } -- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths { +- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { // sort indexPath to avoid messing up the index when inserting in several batches NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; @@ -366,21 +391,21 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]]; } - [self _batchInsertNodes:nodes atIndexPaths:indexPaths]; + [self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption]; } -- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths { +- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { // sort indexPath in order to avoid messing up the index when deleting NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; dispatch_async([ASDataController sizingQueue], ^{ [self asyncUpdateDataWithBlock:^{ - DELETE_NODES(_nodes, sortedIndexPaths); + DELETE_NODES(_nodes, sortedIndexPaths, animationOption); }]; }); } -- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths { +- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { // The reloading operation required reloading the data // Loading data in the calling thread NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count]; @@ -391,10 +416,10 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; dispatch_async([ASDataController sizingQueue], ^{ [self syncUpdateDataWithBlock:^{ - DELETE_NODES(_nodes, indexPaths); + DELETE_NODES(_nodes, indexPaths, animationOption); }]; - [self _batchInsertNodes:nodes atIndexPaths:indexPaths]; + [self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption]; }); } @@ -403,11 +428,11 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [self asyncUpdateDataWithBlock:^{ NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_nodes, [NSArray arrayWithObject:indexPath]); NSArray *indexPaths = [NSArray arrayWithObject:indexPath]; - DELETE_NODES(_nodes, indexPaths); + DELETE_NODES(_nodes, indexPaths, kASDataControllerAnimationOptionNone); // Don't re-calculate size for moving NSArray *newIndexPaths = [NSArray arrayWithObject:newIndexPath]; - INSERT_NODES(_nodes, newIndexPaths, nodes); + INSERT_NODES(_nodes, newIndexPaths, nodes, kASDataControllerAnimationOptionNone); }]; }); } @@ -433,10 +458,10 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [self syncUpdateDataWithBlock:^{ NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes); - DELETE_NODES(_nodes, indexPaths); + DELETE_NODES(_nodes, indexPaths, kASDataControllerAnimationOptionNone); NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)]; - DELETE_SECTIONS(_nodes, indexSet); + DELETE_SECTIONS(_nodes, indexSet, kASDataControllerAnimationOptionNone); // Insert section @@ -446,11 +471,11 @@ static void *kASDataUpdatingQueueContext = &kASDataUpdatingQueueContext; [sections addObject:[[NSMutableArray alloc] init]]; } - INSERT_SECTIONS(_nodes, [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)], sections); + INSERT_SECTIONS(_nodes, [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)], sections, kASDataControllerAnimationOptionNone); }]; - [self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths]; + [self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:kASDataControllerAnimationOptionNone]; }); } diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index d32de1ddfd..41ef85582d 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -70,6 +70,16 @@ */ - (CGSize)rangeControllerViewportSize:(ASRangeController *)rangeController; +/** + * Begin updates. + */ +- (void)rangeControllerBeginUpdates:(ASRangeController *)rangeController; + +/** + * End updates. + */ +- (void)rangeControllerEndUpdates:(ASRangeController * )rangeController; + /** * Fetch nodes at specific index paths. */ @@ -78,43 +88,43 @@ /** * Called for nodes insertion. */ -- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths; +- (void)rangeController:(ASRangeController *)rangeController didInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called for nodes deletion. */ -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths; +- (void)rangeController:(ASRangeController *)rangeController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called for section insertion. */ -- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet; +- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called for section deletion. */ -- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet; +- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; @optional /** * Called before nodes insertion. */ -- (void)rangeController:(ASRangeController *)rangeController willInsertNodesAtIndexPaths:(NSArray *)indexPaths; +- (void)rangeController:(ASRangeController *)rangeController willInsertNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called before nodes deletion. */ -- (void)rangeController:(ASRangeController *)rangeController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths; +- (void)rangeController:(ASRangeController *)rangeController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called before section insertion. */ -- (void)rangeController:(ASRangeController *)rangeController willInsertSectionsAtIndexSet:(NSIndexSet *)indexSet; +- (void)rangeController:(ASRangeController *)rangeController willInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; /** * Called before section deletion. */ -- (void)rangeController:(ASRangeController *)rangeController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet; +- (void)rangeController:(ASRangeController *)rangeController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption; @end diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 4dacf56582..c8c8474af5 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -213,15 +213,27 @@ #pragma mark - ASDataControllerDelegete -- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { +- (void)dataControllerBeginUpdates:(ASDataController *)dataController { ASDisplayNodePerformBlockOnMainThread(^{ - if ([_delegate respondsToSelector:@selector(rangeController:willInsertNodesAtIndexPaths:)]) { - [_delegate rangeController:self willInsertNodesAtIndexPaths:indexPaths]; + [_delegate rangeControllerBeginUpdates:self]; + }); +} + +- (void)dataControllerEndUpdates:(ASDataController *)dataController { + ASDisplayNodePerformBlockOnMainThread(^{ + [_delegate rangeControllerEndUpdates:self]; + }); +} + +- (void)dataController:(ASDataController *)dataController willInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { + ASDisplayNodePerformBlockOnMainThread(^{ + if ([_delegate respondsToSelector:@selector(rangeController:willInsertNodesAtIndexPaths:withAnimationOption:)]) { + [_delegate rangeController:self willInsertNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; } }); } -- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths { +- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path"); NSMutableArray *nodeSizes = [NSMutableArray arrayWithCapacity:nodes.count]; @@ -231,36 +243,36 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController insertNodesAtIndexPaths:indexPaths withSizes:nodeSizes]; - [_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths]; + [_delegate rangeController:self didInsertNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; _workingRangeIsValid = NO; }); } -- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths { +- (void)dataController:(ASDataController *)dataController willDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodePerformBlockOnMainThread(^{ - if ([_delegate respondsToSelector:@selector(rangeController:willDeleteNodesAtIndexPaths:)]) { - [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths]; + if ([_delegate respondsToSelector:@selector(rangeController:willDeleteNodesAtIndexPaths:withAnimationOption:)]) { + [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; } }); } -- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths { +- (void)dataController:(ASDataController *)dataController didDeleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController deleteNodesAtIndexPaths:indexPaths]; - [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths]; + [_delegate rangeController:self didDeleteNodesAtIndexPaths:indexPaths withAnimationOption:animationOption]; _workingRangeIsValid = NO; }); } -- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet { +- (void)dataController:(ASDataController *)dataController willInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodePerformBlockOnMainThread(^{ - if ([_delegate respondsToSelector:@selector(rangeController:willInsertSectionsAtIndexSet:)]) { - [_delegate rangeController:self willInsertSectionsAtIndexSet:indexSet]; + if ([_delegate respondsToSelector:@selector(rangeController:willInsertSectionsAtIndexSet:withAnimationOption:)]) { + [_delegate rangeController:self willInsertSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; } }); } -- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet { +- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections"); NSMutableArray *sectionNodeSizes = [NSMutableArray arrayWithCapacity:sections.count]; @@ -275,23 +287,23 @@ ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController insertSections:sectionNodeSizes atIndexSet:indexSet]; - [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet]; + [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; _workingRangeIsValid = NO; }); } -- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet { +- (void)dataController:(ASDataController *)dataController willDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodePerformBlockOnMainThread(^{ if ([_delegate respondsToSelector:@selector(rangeController:willDeleteSectionsAtIndexSet:)]) { - [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet]; + [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; } }); } -- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet { +- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption { ASDisplayNodePerformBlockOnMainThread(^{ [_layoutController deleteSectionsAtIndexSet:indexSet]; - [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet]; + [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOption:animationOption]; _workingRangeIsValid = NO; }); }