Avoid multiple initial data loads being issued by UICollectionView/UITableView (#3208)

* Avoid multiple initial data loads being issued by UICollectionView/UITableView
- Currently, there is a gap between the moment UICollectionView/UITableView triggers its first data load and when ASDataController finishes processing it. During this gap, the view keeps issuing "initial" loads by calling reloadData and causes its data controller to reload multiple times.
- Fix by immediately forward the first reloadData call to UICollectionView/UITableView before letting its data controller to handle it for real. During the gap, the view thinks that it loaded initial data but is empty, and thus stops triggering initial loads. Once the data controller finished loading, it will call another reloadData on the view which causes the view to swap to a correct state.

* Fix tests

* Use the existing flag of ASDataController

* Explain unit test
This commit is contained in:
Huy Nguyen
2017-03-23 11:14:05 +00:00
committed by GitHub
parent 9129fa73d0
commit 36de258ff9
3 changed files with 15 additions and 3 deletions

View File

@@ -317,6 +317,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (! _dataController.initialReloadDataHasBeenCalled) {
// If this is the first reload, forward to super immediately to prevent it from triggering more "initial" loads while our data controller is working.
_superIsPendingDataLoad = YES;
[super reloadData];
}
void (^batchUpdatesCompletion)(BOOL); void (^batchUpdatesCompletion)(BOOL);
if (completion) { if (completion) {
batchUpdatesCompletion = ^(BOOL) { batchUpdatesCompletion = ^(BOOL) {

View File

@@ -483,6 +483,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if (! _dataController.initialReloadDataHasBeenCalled) {
// If this is the first reload, forward to super immediately to prevent it from triggering more "initial" loads while our data controller is working.
[super reloadData];
}
void (^batchUpdatesCompletion)(BOOL); void (^batchUpdatesCompletion)(BOOL);
if (completion) { if (completion) {
batchUpdatesCompletion = ^(BOOL) { batchUpdatesCompletion = ^(BOOL) {

View File

@@ -606,9 +606,10 @@
[node waitUntilAllUpdatesAreCommitted]; [node waitUntilAllUpdatesAreCommitted];
XCTAssertGreaterThan(node.view.numberOfSections, 0); XCTAssertGreaterThan(node.view.numberOfSections, 0);
// Assert that the beginning of the call pattern is correct. // The first reloadData call helps prevent UITableView from calling it multiple times while ASDataController is working.
// There is currently noise that comes after that we will allow for this test. // The second reloadData call is the real one.
NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)) ]; NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)),
NSStringFromSelector(@selector(reloadData)) ];
XCTAssertEqualObjects(selectors, expectedSelectors); XCTAssertEqualObjects(selectors, expectedSelectors);
[UITableView deswizzleAllInstanceMethods]; [UITableView deswizzleAllInstanceMethods];