Address comments

This commit is contained in:
Li Tan
2015-02-11 19:15:08 -08:00
parent 6a2c472910
commit 831aa47ae0
8 changed files with 226 additions and 211 deletions

View File

@@ -36,6 +36,14 @@
/**
* Initializer.
*
* @discussion If asyncDataFetching is enabled, the `AScollectionView` will fetch data through `collectionView:numberOfRowsInSection:` and
* `collectionView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically
* from calling thread.
* Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for
* large scale data. On another hand, the application code need take the responsibility to avoid data inconsistence. Specifically,
* we will lock the data source through `collectionViewLockDataSource`, and unlock it by `collectionViewUnlockDataSource` after the data fetching.
* The application should not update the data source while the data source is locked, to keep data consistence.
*/
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout asyncDataFetching:(BOOL)asyncDataFetchingEnabled;
@@ -112,25 +120,23 @@
*/
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
@optional
/**
* Indicator to lock the data source for data loading in asyn mode.
* Indicator to lock the data source for data fetching in asyn mode.
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
* due to the data access in async mode.
*
* @param tableView The sender.
* @param collectionView The sender.
*/
- (void)collectionViewLockDataSourceForDataUpdating:(ASCollectionView *)collectionView;
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView;
/**
* Indicator to unlock the data source for data loading in asyn mode.
* Indicator to unlock the data source for data fetching in asyn mode.
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
* due to the data access in async mode.
*
* @param tableView The sender.
* @param collectionView The sender.
*/
- (void)collectionViewUnlockDataSourceForDataUpdating:(ASCollectionView *)collectionView;
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView;
@end

View File

@@ -387,23 +387,23 @@ static BOOL _isInterceptedSelector(SEL sel)
}
}
- (void)dataControllerLockDataSourceForDataUpdating
- (void)dataControllerLockDataSource
{
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
self.asyncDataSourceLocked = YES;
if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSourceForDataUpdating:)]) {
[_asyncDataSource collectionViewLockDataSourceForDataUpdating:self];
if ([_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)]) {
[_asyncDataSource collectionViewLockDataSource:self];
}
}
- (void)dataControllerUnlockDataSourceForDataUpdating
- (void)dataControllerUnlockDataSource
{
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has alredy been unlocked !");
self.asyncDataSourceLocked = NO;
if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSourceForDataUpdating:)]) {
[_asyncDataSource collectionViewUnlockDataSourceForDataUpdating:self];
if ([_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)]) {
[_asyncDataSource collectionViewUnlockDataSource:self];
}
}

View File

@@ -36,6 +36,14 @@
/**
* initializer.
*
* @discussion If asyncDataFetching is enabled, the `ASTableView` will fetch data through `tableView:numberOfRowsInSection:` and
* `tableView:nodeForRowAtIndexPath:` in async mode from background thread. Otherwise, the methods will be invoked synchronically
* from calling thread.
* Enabling asyncDataFetching could avoid blocking main thread for `ASCellNode` allocation, which is frequently reported issue for
* large scale data. On another hand, the application code need take the responsibility to avoid data inconsistence. Specifically,
* we will lock the data source through `tableViewLockDataSource`, and unlock it by `tableViewUnlockDataSource` after the data fetching.
* The application should not update the data source while the data source is locked, to keep data consistence.
*/
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled;
@@ -114,25 +122,23 @@
*/
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional
/**
* Indicator to lock the data source for data loading in asyn mode.
* Indicator to lock the data source for data fetching in asyn mode.
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
* due to the data access in async mode.
*
* @param tableView The sender.
*/
- (void)tableViewLockDataSourceForDataUpdating:(ASTableView *)tableView;
- (void)tableViewLockDataSource:(ASTableView *)tableView;
/**
* Indicator to unlock the data source for data loading in asyn mode.
* Indicator to unlock the data source for data fetching in asyn mode.
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistence or exception
* due to the data access in async mode.
*
* @param tableView The sender.
*/
- (void)tableViewUnlockDataSourceForDataUpdating:(ASTableView *)tableView;
- (void)tableViewUnlockDataSource:(ASTableView *)tableView;
@end

View File

@@ -114,7 +114,7 @@ static BOOL _isInterceptedSelector(SEL sel)
BOOL _asyncDataFetchingEnabled;
}
@property (atomic, assign) BOOL asyncDataSouceLocked;
@property (atomic, assign) BOOL asyncDataSourceLocked;
@end
@@ -148,7 +148,7 @@ static BOOL _isInterceptedSelector(SEL sel)
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
_asyncDataSouceLocked = NO;
_asyncDataSourceLocked = NO;
return self;
}
@@ -177,9 +177,6 @@ static BOOL _isInterceptedSelector(SEL sel)
_proxyDataSource = nil;
super.dataSource = nil;
} else {
ASDisplayNodeAssert(!_asyncDataFetchingEnabled || ([asyncDataSource respondsToSelector:@selector(tableViewLockDataSourceForDataUpdating:)] &&
[asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSourceForDataUpdating:)]),
@"The asyncDataSource need to implements \"tableViewLockDataSourceForDataUpdating\" and \"tableViewUnlockDataSourceForDataUpdating\" to handle data fetching in async mode.");
_asyncDataSource = asyncDataSource;
_proxyDataSource = [[_ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
@@ -441,25 +438,25 @@ static BOOL _isInterceptedSelector(SEL sel)
return CGSizeMake(self.bounds.size.width, FLT_MAX);
}
- (void)dataControllerLockDataSourceForDataUpdating
- (void)dataControllerLockDataSource
{
ASDisplayNodeAssert(!self.asyncDataSouceLocked, @"The data source has already been locked !");
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked !");
self.asyncDataSouceLocked = YES;
self.asyncDataSourceLocked = YES;
if ([_asyncDataSource respondsToSelector:@selector(tableViewLockDataSourceForDataUpdating:)]) {
[_asyncDataSource tableViewLockDataSourceForDataUpdating:self];
if ([_asyncDataSource respondsToSelector:@selector(tableViewLockDataSource:)]) {
[_asyncDataSource tableViewLockDataSource:self];
}
}
- (void)dataControllerUnlockDataSourceForDataUpdating
- (void)dataControllerUnlockDataSource
{
ASDisplayNodeAssert(self.asyncDataSouceLocked, @"The data source has already been unlocked !");
ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked !");
self.asyncDataSouceLocked = NO;
self.asyncDataSourceLocked = NO;
if ([_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSourceForDataUpdating:)]) {
[_asyncDataSource tableViewUnlockDataSourceForDataUpdating:self];
if ([_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSource:)]) {
[_asyncDataSource tableViewUnlockDataSource:self];
}
}

View File

@@ -3,19 +3,6 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
/**
Enable the data fetching in async mode.
If enabled, we will fetch data through `dataController:nodeAtIndexPath:` and `dataController:rowsInSection:` in background thread.
Otherwise, the methods will be invoked synchronically in calling thread.
Enabling data fetching in async mode could avoid blocking main thread while allocating cell on main thread, which is frequently reported
issue for handing large scale data. On another hand, the application code will take the responsibility to avoid data inconsistence.
Specifically, we will lock the data source through `dataControllerLockDataSourceForDataUpdating`, and unlock it by `dataControllerUnlockDataSourceForDataUpdating`
after the data fetching. The application should not update the data source while the data source is locked.
*/
#define ENABLE_ASYNC_DATA_FETCHING 1
@class ASCellNode;
@class ASDataController;
@@ -50,12 +37,12 @@ typedef NSUInteger ASDataControllerAnimationOptions;
/**
Lock the data source for data fetching.
*/
- (void)dataControllerLockDataSourceForDataUpdating;
- (void)dataControllerLockDataSource;
/**
Unlock the data source after data fetching.
*/
- (void)dataControllerUnlockDataSourceForDataUpdating;
- (void)dataControllerUnlockDataSource;
@end
@@ -120,7 +107,16 @@ typedef NSUInteger ASDataControllerAnimationOptions;
@property (nonatomic, weak) id<ASDataControllerDelegate> delegate;
/**
Designated iniailizer.
* Designated iniailizer.
*
* @param asyncDataFetchingEnabled Enable the data fetching in async mode.
* @discussion If enabled, we will fetch data through `dataController:nodeAtIndexPath:` and `dataController:rowsInSection:` in background thread.
* Otherwise, the methods will be invoked synchronically in calling thread. Enabling data fetching in async mode could avoid blocking main thread
* while allocating cell on main thread, which is frequently reported issue for handing large scale data. On another hand, the application code
* will take the responsibility to avoid data inconsistence. Specifically, we will lock the data source through `dataControllerLockDataSource`,
* and unlock it by `dataControllerUnlockDataSource` after the data fetching. The application should not update the data source while
* the data source is locked.
*/
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled;

View File

@@ -10,25 +10,6 @@
#import "ASMultidimensionalArrayUtils.h"
#import "ASDisplayNodeInternal.h"
#ifdef ENABLE_ASYNC_DATA_FETCHING
#define BEGIN_DATA_FETCHING \
dispatch_block_t block = ^{
#define END_DATA_FETCHING \
}; \
if (_asyncDataFetchingEnabled) { \
[_dataSource dataControllerLockDataSourceForDataUpdating]; \
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ \
block(); \
[_dataSource dataControllerUnlockDataSourceForDataUpdating]; \
}); \
} else { \
block(); \
}
#else
#define BEGIN_DATA_FETCHING
#define END_DATA_FETCHING
#endif
#define INSERT_NODES(multidimensionalArray, indexPath, elements, animationOption) \
{ \
if ([_delegate respondsToSelector:@selector(dataController:willInsertNodes:atIndexPaths:withAnimationOption:)]) { \
@@ -150,31 +131,41 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
});
}
- (void)performDataFetchingWithBlock:(dispatch_block_t)block {
if (_asyncDataFetchingEnabled) {
[_dataSource dataControllerLockDataSource];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
block();
[_dataSource dataControllerUnlockDataSource];
});
} else {
block();
}
}
#pragma mark - Initial Data Loading
- (void)initialDataLoadingWithAnimationOption:(ASDataControllerAnimationOptions)animationOption {
BEGIN_DATA_FETCHING
[self performDataFetchingWithBlock:^{
NSMutableArray *indexPaths = [NSMutableArray array];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
NSMutableArray *indexPaths = [NSMutableArray array];
// insert sections
[self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOption:0];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *indexPath = [[NSIndexPath alloc] initWithIndex:i];
// insert sections
[self insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionNum)] withAnimationOption: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]];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
[indexPaths addObject:[indexPath indexPathByAddingIndex:j]];
}
}
}
// insert elements
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
// insert elements
[self insertRowsAtIndexPaths:indexPaths withAnimationOption:animationOption];
END_DATA_FETCHING
}];
}
#pragma mark - Data Update
@@ -212,45 +203,43 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)insertSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
BEGIN_DATA_FETCHING
__block int nodeTotalCnt = 0;
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger cnt = [_dataSource dataController:self rowsInSection:idx];
[nodeCounts addObject:@(cnt)];
nodeTotalCnt += cnt;
}];
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
__block NSUInteger idx = 0;
[indexSet enumerateIndexesUsingBlock:^(NSUInteger sectionIdx, BOOL *stop) {
NSUInteger cnt = [nodeCounts[idx++] unsignedIntegerValue];
for (int i = 0; i < cnt; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIdx];
[indexPaths addObject:indexPath];
ASCellNode *node = [_dataSource dataController:self nodeAtIndexPath:indexPath];
[nodes addObject:node];
}
}];
dispatch_async([[self class] sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count];
for (NSUInteger i = 0; i < indexSet.count; i++) {
[sectionArray addObject:[NSMutableArray array]];
}
INSERT_SECTIONS(_nodes , indexSet, sectionArray, animationOption);
[self performDataFetchingWithBlock:^{
__block int nodeTotalCnt = 0;
NSMutableArray *nodeCounts = [NSMutableArray arrayWithCapacity:indexSet.count];
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger cnt = [_dataSource dataController:self rowsInSection:idx];
[nodeCounts addObject:@(cnt)];
nodeTotalCnt += cnt;
}];
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:nodeTotalCnt];
END_DATA_FETCHING
__block NSUInteger idx = 0;
[indexSet enumerateIndexesUsingBlock:^(NSUInteger sectionIdx, BOOL *stop) {
NSUInteger cnt = [nodeCounts[idx++] unsignedIntegerValue];
for (int i = 0; i < cnt; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIdx];
[indexPaths addObject:indexPath];
ASCellNode *node = [_dataSource dataController:self nodeAtIndexPath:indexPath];
[nodes addObject:node];
}
}];
dispatch_async([[self class] sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:indexSet.count];
for (NSUInteger i = 0; i < indexSet.count; i++) {
[sectionArray addObject:[NSMutableArray array]];
}
INSERT_SECTIONS(_nodes , indexSet, sectionArray, animationOption);
}];
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
}];
}
- (void)deleteSections:(NSIndexSet *)indexSet withAnimationOption:(ASDataControllerAnimationOptions)animationOption
@@ -268,35 +257,33 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)reloadSections:(NSIndexSet *)sections withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
BEGIN_DATA_FETCHING
[self performDataFetchingWithBlock:^{
// We need to keep data query on data source in the calling thread.
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
// We need to keep data query on data source in the calling thread.
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
[sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:idx];
[sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:idx];
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
for (NSUInteger i = 0; i < rowNum; i++) {
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}];
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
// remove elements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections);
DELETE_NODES(_nodes, indexPaths, animationOption);
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
for (NSUInteger i = 0; i < rowNum; i++) {
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}];
// reinsert the elements
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
// remove elements
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_nodes, sections);
DELETE_NODES(_nodes, indexPaths, animationOption);
}];
END_DATA_FETCHING
// reinsert the elements
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
}];
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOption:(ASDataControllerAnimationOptions)animationOption
@@ -382,18 +369,16 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
BEGIN_DATA_FETCHING
[self performDataFetchingWithBlock:^{
// 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];
for (NSUInteger i = 0; i < sortedIndexPaths.count; i++) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
}
// 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];
for (NSUInteger i = 0; i < sortedIndexPaths.count; i++) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:sortedIndexPaths[i]]];
}
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
END_DATA_FETCHING
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
}];
}
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
@@ -410,25 +395,23 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
BEGIN_DATA_FETCHING
// The reloading operation required reloading the data
// Loading data in the calling thread
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}];
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
DELETE_NODES(_nodes, indexPaths, animationOption);
[self performDataFetchingWithBlock:^{
// The reloading operation required reloading the data
// Loading data in the calling thread
NSMutableArray *nodes = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
[indexPaths sortedArrayUsingSelector:@selector(compare:)];
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[nodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}];
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
DELETE_NODES(_nodes, indexPaths, animationOption);
}];
END_DATA_FETCHING
[self _batchInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOption];
});
}];
}
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOption:(ASDataControllerAnimationOptions)animationOption
@@ -448,49 +431,47 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)reloadDataWithAnimationOption:(ASDataControllerAnimationOptions)animationOption
{
BEGIN_DATA_FETCHING
[self performDataFetchingWithBlock:^{
// Fetching data in calling thread
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
// Fetching data in calling thread
NSMutableArray *updatedNodes = [[NSMutableArray alloc] init];
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] init];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger sectionNum = [_dataSource dataControllerNumberOfSections:self];
for (NSUInteger i = 0; i < sectionNum; i++) {
NSIndexPath *sectionIndexPath = [[NSIndexPath alloc] initWithIndex:i];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes);
DELETE_NODES(_nodes, indexPaths, animationOption);
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)];
DELETE_SECTIONS(_nodes, indexSet, animationOption);
// Insert section
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:sectionNum];
for (int i = 0; i < sectionNum; i++) {
[sections addObject:[[NSMutableArray alloc] init]];
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:i];
for (NSUInteger j = 0; j < rowNum; j++) {
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
[updatedIndexPaths addObject:indexPath];
[updatedNodes addObject:[_dataSource dataController:self nodeAtIndexPath:indexPath]];
}
}
INSERT_SECTIONS(_nodes, [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)], sections, animationOption);
dispatch_async([ASDataController sizingQueue], ^{
[self syncUpdateDataWithBlock:^{
}];
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_nodes);
DELETE_NODES(_nodes, indexPaths, animationOption);
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _nodes.count)];
DELETE_SECTIONS(_nodes, indexSet, animationOption);
END_DATA_FETCHING
// Insert section
NSMutableArray *sections = [[NSMutableArray alloc] initWithCapacity:sectionNum];
for (int i = 0; i < sectionNum; i++) {
[sections addObject:[[NSMutableArray alloc] init]];
}
INSERT_SECTIONS(_nodes, [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, sectionNum)], sections, animationOption);
}];
[self _batchInsertNodes:updatedNodes atIndexPaths:updatedIndexPaths withAnimationOptions:animationOption];
});
}];
}
#pragma mark - Data Querying

View File

@@ -34,7 +34,7 @@
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES];
_collectionView.asyncDataSource = self;
_collectionView.asyncDelegate = self;
_collectionView.backgroundColor = [UIColor whiteColor];
@@ -78,4 +78,13 @@
return 300;
}
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView {
// lock the data source
// The data source should not be change until it is unlocked.
}
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView {
// unlock the data source to enable data source updating.
}
@end

View File

@@ -12,6 +12,7 @@
#import "ViewController.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <AsyncDisplayKit/ASAssert.h>
#import "BlurbNode.h"
#import "KittenNode.h"
@@ -25,8 +26,13 @@ static const NSInteger kLitterSize = 20;
// array of boxed CGSizes corresponding to placekitten kittens
NSArray *_kittenDataSource;
BOOL _dataSourceLocked;
}
@property (nonatomic, strong) NSArray *kittenDataSource;
@property (atomic, assign) BOOL dataSourceLocked;
@end
@@ -40,7 +46,7 @@ static const NSInteger kLitterSize = 20;
if (!(self = [super init]))
return nil;
_tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
_tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain asyncDataFetching:YES];
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // KittenNode has its own separator
_tableView.asyncDataSource = self;
_tableView.asyncDelegate = self;
@@ -59,6 +65,12 @@ static const NSInteger kLitterSize = 20;
return self;
}
- (void)setKittenDataSource:(NSArray *)kittenDataSource {
ASDisplayNodeAssert(!self.dataSourceLocked, @"Could not update data source when it is locked !");
_kittenDataSource = kittenDataSource;
}
- (void)viewDidLoad
{
[super viewDidLoad];
@@ -105,4 +117,12 @@ static const NSInteger kLitterSize = 20;
return NO;
}
- (void)tableViewLockDataSource:(ASTableView *)tableView {
self.dataSourceLocked = YES;
}
- (void)tableViewUnlockDataSource:(ASTableView *)tableView {
self.dataSourceLocked = NO;
}
@end