From 209425c22ce84ecbb7ab631753ec8fc34dd9cbb0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Sun, 24 Jan 2016 22:49:52 -0800 Subject: [PATCH] Revert commit 83aaa46, thus bring 2e4d716 back --- AsyncDisplayKit/ASCollectionView.mm | 23 ++- AsyncDisplayKit/ASTableView.mm | 20 ++- AsyncDisplayKit/Details/ASDataController.h | 9 +- AsyncDisplayKit/Details/ASDataController.mm | 133 ++++++++++++++---- AsyncDisplayKit/Details/ASRangeController.h | 7 + AsyncDisplayKit/Details/ASRangeController.mm | 13 ++ .../Details/ASRangeControllerBeta.mm | 8 ++ 7 files changed, 178 insertions(+), 35 deletions(-) diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index 2b588d088b..4334ee5d70 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -250,7 +250,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; _superIsPendingDataLoad = YES; [super reloadData]; }); - [_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion]; + [_dataController reloadDataWithCompletion:completion]; } - (void)reloadData @@ -262,7 +262,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; { ASDisplayNodeAssertMainThread(); _superIsPendingDataLoad = YES; - [_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone]; + [_dataController reloadDataImmediately]; [super reloadData]; } @@ -938,6 +938,25 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; } } +- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController +{ + ASDisplayNodeAssertMainThread(); + + if (!self.asyncDataSource || _superIsPendingDataLoad) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + if (_performingBatchUpdates) { + [_batchUpdateBlocks addObject:^{ + [super reloadData]; + }]; + } else { + [UIView performWithoutAnimation:^{ + [super reloadData]; + }]; + } +} + #pragma mark - ASCellNodeDelegate - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 6d5c40e807..26248fb8b6 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -298,10 +298,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)reloadDataWithCompletion:(void (^)())completion { - ASPerformBlockOnMainThread(^{ - [super reloadData]; - }); - [_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion]; + [_dataController reloadDataWithCompletion:completion]; } - (void)reloadData @@ -312,8 +309,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); - [_dataController reloadDataImmediatelyWithAnimationOptions:UITableViewRowAnimationNone]; - [super reloadData]; + [_dataController reloadDataImmediately]; } - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType @@ -845,6 +841,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; }); } +- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController +{ + ASDisplayNodeAssertMainThread(); + LOG(@"UITableView reloadData"); + + if (!self.asyncDataSource) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + [super reloadData]; +} + #pragma mark - ASDataControllerDelegate - (ASCellNode *)dataController:(ASDataController *)dataController nodeAtIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/Details/ASDataController.h b/AsyncDisplayKit/Details/ASDataController.h index 5710b22b4e..113c582675 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/AsyncDisplayKit/Details/ASDataController.h @@ -94,6 +94,11 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind; */ - (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +/** + Called for data reload. + */ +- (void)dataControllerDidReloadData:(ASDataController *)dataController; + @end /** @@ -170,9 +175,9 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind; - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; -- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^ _Nullable)())completion; +- (void)reloadDataWithCompletion:(void (^ _Nullable)())completion; -- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +- (void)reloadDataImmediately; /** @name Data Querying */ diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index 53b3715874..2b1549b651 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -43,6 +43,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; BOOL _delegateDidDeleteNodes; BOOL _delegateDidInsertSections; BOOL _delegateDidDeleteSections; + BOOL _delegateDidReloadData; } @property (atomic, assign) NSUInteger batchUpdateCounter; @@ -92,6 +93,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; _delegateDidDeleteNodes = [_delegate respondsToSelector:@selector(dataController:didDeleteNodes:atIndexPaths:withAnimationOptions:)]; _delegateDidInsertSections = [_delegate respondsToSelector:@selector(dataController:didInsertSections:atIndexSet:withAnimationOptions:)]; _delegateDidDeleteSections = [_delegate respondsToSelector:@selector(dataController:didDeleteSectionsAtIndexSet:withAnimationOptions:)]; + _delegateDidReloadData = [_delegate respondsToSelector:@selector(dataControllerDidReloadData:)]; } + (NSUInteger)parallelProcessorCount @@ -143,14 +145,30 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height); } +/** + * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. + */ +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths +{ + [self _batchLayoutNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:NO withAnimationOptions:0]; +} + /** * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. */ - (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + [self _batchLayoutNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:YES withAnimationOptions:animationOptions]; +} + +/** + * Measures and defines the layout for each node in optimized batches on an editing queue, inserting the results into the backing store. + */ +- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { [self batchLayoutNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { // Insert finished nodes into data storage - [self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + [self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:shouldNotifyDelegate withAnimationOptions:animationOptions]; }]; } @@ -240,6 +258,14 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +- (void)deleteAllNodesOfKind:(NSString *)kind +{ + [_editingNodes[kind] removeAllObjects]; + [_mainSerialQueue performBlockOnMainThread:^{ + [_completedNodes[kind] removeAllObjects]; + }]; +} + - (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock { if (indexSet.count == 0) @@ -277,6 +303,17 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; #pragma mark - Internal Data Querying + Editing +/** + * Inserts the specified nodes into the given index paths and doesn't notify the delegate of newly inserted nodes. + * + * @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy + * of the editing nodes. + */ +- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths +{ + [self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:NO withAnimationOptions:0]; +} + /** * Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes. * @@ -285,10 +322,26 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; */ - (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) { - if (_delegateDidInsertNodes) - [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; - }]; + [self _insertNodes:nodes atIndexPaths:indexPaths andNotifyDelegate:YES withAnimationOptions:animationOptions]; +} + +/** + * Inserts the specified nodes into the given index paths and notifies the delegate of newly inserted nodes. + * + * @discussion Nodes are first inserted into the editing store, then the completed store is replaced by a deep copy + * of the editing nodes. The delegate is invoked on the main thread. + */ +- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + void (^completionBlock)(NSArray *nodes, NSArray *indexPaths) = nil; + if (shouldNotifyDelegate) { + completionBlock = ^(NSArray *nodes, NSArray *indexPaths) { + if (_delegateDidInsertNodes) + [_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; + }; + } + + [self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:completionBlock]; } /** @@ -305,18 +358,46 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } +/** + * Inserts sections, represented as arrays, into the backing store at the given indicies and doesn't notify the delegate. + * + * @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted + * in the completed store on the main thread. + */ +- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet +{ + [self _insertSections:sections atIndexSet:indexSet andNotifyDelegate:NO withAnimationOptions:0]; +} + +/** + * Inserts sections, represented as arrays, into the backing store at the given indicies and doesn't notify the delegate. + * + * @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted + * in the completed store on the main thread. + */ +- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +{ + [self _insertSections:sections atIndexSet:indexSet andNotifyDelegate:YES withAnimationOptions:animationOptions]; +} + /** * Inserts sections, represented as arrays, into the backing store at the given indicies and notifies the delegate. * * @discussion The section arrays are inserted into the editing store, then a deep copy of the sections are inserted * in the completed store on the main thread. The delegate is invoked on the main thread. */ -- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet andNotifyDelegate:(BOOL)shouldNotifyDelegate withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions { - [self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) { - if (_delegateDidInsertSections) - [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; - }]; + + void (^completionBlock)(NSArray *sections, NSIndexSet *indexSet) = nil; + if (shouldNotifyDelegate) { + completionBlock = ^(NSArray *sections, NSIndexSet *indexSet) { + if (_delegateDidInsertSections) + [_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions]; + }; + } + + [self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:completionBlock]; } /** @@ -361,17 +442,17 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; }]; } -- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^)())completion +- (void)reloadDataWithCompletion:(void (^)())completion { - [self _reloadDataWithAnimationOptions:animationOptions synchronously:NO completion:completion]; + [self _reloadDataSynchronously:NO completion:completion]; } -- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)reloadDataImmediately { - [self _reloadDataWithAnimationOptions:animationOptions synchronously:YES completion:nil]; + [self _reloadDataSynchronously:YES completion:nil]; } -- (void)_reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions synchronously:(BOOL)synchronously completion:(void (^)())completion +- (void)_reloadDataSynchronously:(BOOL)synchronously completion:(void (^)())completion { [self performEditCommandWithBlock:^{ ASDisplayNodeAssertMainThread(); @@ -393,12 +474,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; LOG(@"Edit Transaction - reloadData"); // 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]; - NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)]; - [self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [self deleteAllNodesOfKind:ASDataControllerRowNodeKind]; [self willReloadData]; @@ -408,12 +484,19 @@ static void *kASSizingQueueContext = &kASSizingQueueContext; [sections addObject:[[NSMutableArray alloc] init]]; } - [self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] withAnimationOptions:animationOptions]; + [self _insertSections:sections atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]]; - [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOptions]; + [self _batchLayoutNodes:updatedNodes atIndexPaths:updatedIndexPaths]; - if (completion) { - dispatch_async(dispatch_get_main_queue(), completion); + if (_delegateDidReloadData || completion) { + [_mainSerialQueue performBlockOnMainThread:^{ + if (_delegateDidReloadData) { + [_delegate dataControllerDidReloadData:self]; + } + if (completion) { + completion(); + } + }]; } }; diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/AsyncDisplayKit/Details/ASRangeController.h index 74061f8806..61ee2470ba 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/AsyncDisplayKit/Details/ASRangeController.h @@ -190,6 +190,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +/** + * Called for data reload. + * + * @param rangeController Sender. + */ +- (void)rangeControllerDidReloadData:(ASRangeController *)rangeController; + @end NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/AsyncDisplayKit/Details/ASRangeController.mm index 0329ccfdf4..fa0610a0a4 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/AsyncDisplayKit/Details/ASRangeController.mm @@ -276,4 +276,17 @@ }); } +- (void)dataControllerDidReloadData:(ASDataController *)dataController +{ + ASPerformBlockOnMainThread(^{ + _rangeIsValid = NO; + + // When reload data we need to make sure that _rangeTypeIndexPaths is cleared as well, + // otherwise _updateVisibleNodeIndexPaths may try to retrieve nodes from dataSource that aren't there anymore + [_rangeTypeIndexPaths removeAllObjects]; + + [_delegate rangeControllerDidReloadData:self]; + }); +} + @end diff --git a/AsyncDisplayKit/Details/ASRangeControllerBeta.mm b/AsyncDisplayKit/Details/ASRangeControllerBeta.mm index dd755d5ca2..5e68002e4d 100644 --- a/AsyncDisplayKit/Details/ASRangeControllerBeta.mm +++ b/AsyncDisplayKit/Details/ASRangeControllerBeta.mm @@ -288,4 +288,12 @@ }); } +- (void)dataControllerDidReloadData:(ASDataController *)dataController +{ + ASPerformBlockOnMainThread(^{ + _rangeIsValid = NO; + [_delegate rangeControllerDidReloadData:self]; + }); +} + @end