mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[ASDataController] Remove asyncDataFetching Option, Cleanup (#1794)
* [ASDataController] Add some assertions to clarify what queues things happen on * [ASCollectionDataController] Optimize willReloadData * [ASDataController] Minor optimizations, no functional changes * [ASDataController] Always reload data on _editingTransactionQueue * [ASDataController] Remove async data fetching option, deprecate callbacks * [ASDataController] Not mutable * [ASMultidimensionalArrayUtils] Use fast enumeration * Optimize ASMultidimensionalArrayUtils
This commit is contained in:
@@ -407,8 +407,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* due to the data access in async mode.
|
* due to the data access in async mode.
|
||||||
*
|
*
|
||||||
* @param collectionView The sender.
|
* @param collectionView The sender.
|
||||||
|
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||||
*/
|
*/
|
||||||
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView;
|
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicator to unlock the data source for data fetching in async mode.
|
* Indicator to unlock the data source for data fetching in async mode.
|
||||||
@@ -416,8 +417,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* due to the data access in async mode.
|
* due to the data access in async mode.
|
||||||
*
|
*
|
||||||
* @param collectionView The sender.
|
* @param collectionView The sender.
|
||||||
|
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||||
*/
|
*/
|
||||||
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView;
|
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
BOOL _performingBatchUpdates;
|
BOOL _performingBatchUpdates;
|
||||||
NSMutableArray *_batchUpdateBlocks;
|
NSMutableArray *_batchUpdateBlocks;
|
||||||
|
|
||||||
BOOL _asyncDataFetchingEnabled;
|
|
||||||
_ASCollectionViewNodeSizeInvalidationContext *_queuedNodeSizeInvalidationContext; // Main thread only
|
_ASCollectionViewNodeSizeInvalidationContext *_queuedNodeSizeInvalidationContext; // Main thread only
|
||||||
BOOL _isDeallocating;
|
BOOL _isDeallocating;
|
||||||
|
|
||||||
@@ -155,14 +154,10 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
unsigned int asyncDataSourceNodeForItemAtIndexPath:1;
|
unsigned int asyncDataSourceNodeForItemAtIndexPath:1;
|
||||||
unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1;
|
unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1;
|
||||||
unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1;
|
unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1;
|
||||||
unsigned int asyncDataSourceCollectionViewLockDataSource:1;
|
|
||||||
unsigned int asyncDataSourceCollectionViewUnlockDataSource:1;
|
|
||||||
unsigned int asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath:1;
|
unsigned int asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath:1;
|
||||||
} _asyncDataSourceFlags;
|
} _asyncDataSourceFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
|
||||||
|
|
||||||
// Used only when ASCollectionView is created directly rather than through ASCollectionNode.
|
// Used only when ASCollectionView is created directly rather than through ASCollectionNode.
|
||||||
// We create a node so that logic related to appearance, memory management, etc can be located there
|
// We create a node so that logic related to appearance, memory management, etc can be located there
|
||||||
// for both the node-based and view-based version of the table.
|
// for both the node-based and view-based version of the table.
|
||||||
@@ -231,7 +226,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
_rangeController.delegate = self;
|
_rangeController.delegate = self;
|
||||||
_rangeController.layoutController = _layoutController;
|
_rangeController.layoutController = _layoutController;
|
||||||
|
|
||||||
_dataController = [[ASCollectionDataController alloc] initWithAsyncDataFetching:NO];
|
_dataController = [[ASCollectionDataController alloc] init];
|
||||||
_dataController.delegate = _rangeController;
|
_dataController.delegate = _rangeController;
|
||||||
_dataController.dataSource = self;
|
_dataController.dataSource = self;
|
||||||
_dataController.environmentDelegate = self;
|
_dataController.environmentDelegate = self;
|
||||||
@@ -240,9 +235,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
|
|
||||||
_leadingScreensForBatching = 2.0;
|
_leadingScreensForBatching = 2.0;
|
||||||
|
|
||||||
_asyncDataFetchingEnabled = NO;
|
|
||||||
_asyncDataSourceLocked = NO;
|
|
||||||
|
|
||||||
_performingBatchUpdates = NO;
|
_performingBatchUpdates = NO;
|
||||||
_batchUpdateBlocks = [NSMutableArray array];
|
_batchUpdateBlocks = [NSMutableArray array];
|
||||||
|
|
||||||
@@ -375,9 +367,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
_asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)];
|
_asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)];
|
_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)];
|
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(collectionViewLockDataSource:)];
|
_asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource = [_asyncDataSource respondsToSelector:@selector(collectionViewUnlockDataSource:)];
|
|
||||||
_asyncDataSourceFlags.asyncDataSourceCollectionViewConstrainedSizeForNodeAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];;
|
|
||||||
|
|
||||||
// Data-source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath:
|
// Data-source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath:
|
||||||
ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath || _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath);
|
ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath || _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath);
|
||||||
@@ -937,26 +927,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataControllerLockDataSource
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
|
|
||||||
|
|
||||||
self.asyncDataSourceLocked = YES;
|
|
||||||
if (_asyncDataSourceFlags.asyncDataSourceCollectionViewLockDataSource) {
|
|
||||||
[_asyncDataSource collectionViewLockDataSource:self];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dataControllerUnlockDataSource
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked");
|
|
||||||
|
|
||||||
self.asyncDataSourceLocked = NO;
|
|
||||||
if (_asyncDataSourceFlags.asyncDataSourceCollectionViewUnlockDataSource) {
|
|
||||||
[_asyncDataSource collectionViewUnlockDataSource:self];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<ASEnvironment>)dataControllerEnvironment
|
- (id<ASEnvironment>)dataControllerEnvironment
|
||||||
{
|
{
|
||||||
if (self.collectionNode) {
|
if (self.collectionNode) {
|
||||||
|
|||||||
@@ -44,18 +44,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* The frame of the table view changes as table cells are added and deleted.
|
* The frame of the table view changes as table cells are added and deleted.
|
||||||
*
|
*
|
||||||
* @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.
|
* @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.
|
||||||
*
|
|
||||||
* @param asyncDataFetchingEnabled This option is reserved for future use, and currently a no-op.
|
|
||||||
*
|
|
||||||
* @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;
|
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tuning parameters for a range type in full mode.
|
* Tuning parameters for a range type in full mode.
|
||||||
@@ -363,8 +353,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* due to the data access in async mode.
|
* due to the data access in async mode.
|
||||||
*
|
*
|
||||||
* @param tableView The sender.
|
* @param tableView The sender.
|
||||||
|
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||||
*/
|
*/
|
||||||
- (void)tableViewLockDataSource:(ASTableView *)tableView;
|
- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicator to unlock the data source for data fetching in asyn mode.
|
* Indicator to unlock the data source for data fetching in asyn mode.
|
||||||
@@ -372,8 +363,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
* due to the data access in async mode.
|
* due to the data access in async mode.
|
||||||
*
|
*
|
||||||
* @param tableView The sender.
|
* @param tableView The sender.
|
||||||
|
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||||
*/
|
*/
|
||||||
- (void)tableViewUnlockDataSource:(ASTableView *)tableView;
|
- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -458,4 +450,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
@protocol ASTableViewDelegate <ASTableDelegate>
|
@protocol ASTableViewDelegate <ASTableDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface ASTableView (Deprecated)
|
||||||
|
|
||||||
|
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style asyncDataFetching:(BOOL)asyncDataFetchingEnabled ASDISPLAYNODE_DEPRECATED;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|||||||
@@ -100,8 +100,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
|
|
||||||
ASRangeController *_rangeController;
|
ASRangeController *_rangeController;
|
||||||
|
|
||||||
BOOL _asyncDataFetchingEnabled;
|
|
||||||
|
|
||||||
ASBatchContext *_batchContext;
|
ASBatchContext *_batchContext;
|
||||||
|
|
||||||
NSIndexPath *_pendingVisibleIndexPath;
|
NSIndexPath *_pendingVisibleIndexPath;
|
||||||
@@ -133,12 +131,9 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
unsigned int asyncDataSourceNumberOfSectionsInTableView:1;
|
unsigned int asyncDataSourceNumberOfSectionsInTableView:1;
|
||||||
unsigned int asyncDataSourceTableViewNodeBlockForRowAtIndexPath:1;
|
unsigned int asyncDataSourceTableViewNodeBlockForRowAtIndexPath:1;
|
||||||
unsigned int asyncDataSourceTableViewNodeForRowAtIndexPath:1;
|
unsigned int asyncDataSourceTableViewNodeForRowAtIndexPath:1;
|
||||||
unsigned int asyncDataSourceTableViewLockDataSource:1;
|
|
||||||
unsigned int asyncDataSourceTableViewUnlockDataSource:1;
|
|
||||||
} _asyncDataSourceFlags;
|
} _asyncDataSourceFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (atomic, assign) BOOL asyncDataSourceLocked;
|
|
||||||
@property (nonatomic, strong, readwrite) ASDataController *dataController;
|
@property (nonatomic, strong, readwrite) ASDataController *dataController;
|
||||||
|
|
||||||
// Used only when ASTableView is created directly rather than through ASTableNode.
|
// Used only when ASTableView is created directly rather than through ASTableNode.
|
||||||
@@ -177,16 +172,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
_rangeController.dataSource = self;
|
_rangeController.dataSource = self;
|
||||||
_rangeController.delegate = self;
|
_rangeController.delegate = self;
|
||||||
|
|
||||||
_dataController = [[dataControllerClass alloc] initWithAsyncDataFetching:NO];
|
_dataController = [[dataControllerClass alloc] init];
|
||||||
_dataController.dataSource = self;
|
_dataController.dataSource = self;
|
||||||
_dataController.delegate = _rangeController;
|
_dataController.delegate = _rangeController;
|
||||||
_dataController.environmentDelegate = self;
|
_dataController.environmentDelegate = self;
|
||||||
|
|
||||||
_layoutController.dataSource = _dataController;
|
_layoutController.dataSource = _dataController;
|
||||||
|
|
||||||
_asyncDataFetchingEnabled = NO;
|
|
||||||
_asyncDataSourceLocked = NO;
|
|
||||||
|
|
||||||
_leadingScreensForBatching = 2.0;
|
_leadingScreensForBatching = 2.0;
|
||||||
_batchContext = [[ASBatchContext alloc] init];
|
_batchContext = [[ASBatchContext alloc] init];
|
||||||
|
|
||||||
@@ -290,8 +282,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
|
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
|
_asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
|
_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
|
||||||
_asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewLockDataSource:)];
|
|
||||||
_asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource = [_asyncDataSource respondsToSelector:@selector(tableViewUnlockDataSource:)];
|
|
||||||
|
|
||||||
// Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath:
|
// Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath:
|
||||||
ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath || _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath);
|
ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath || _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath);
|
||||||
@@ -1083,28 +1073,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
|||||||
CGSizeMake(_nodesConstrainedWidth, FLT_MAX));
|
CGSizeMake(_nodesConstrainedWidth, FLT_MAX));
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataControllerLockDataSource
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(!self.asyncDataSourceLocked, @"The data source has already been locked");
|
|
||||||
|
|
||||||
self.asyncDataSourceLocked = YES;
|
|
||||||
|
|
||||||
if (_asyncDataSourceFlags.asyncDataSourceTableViewLockDataSource) {
|
|
||||||
[_asyncDataSource tableViewLockDataSource:self];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)dataControllerUnlockDataSource
|
|
||||||
{
|
|
||||||
ASDisplayNodeAssert(self.asyncDataSourceLocked, @"The data source has already been unlocked");
|
|
||||||
|
|
||||||
self.asyncDataSourceLocked = NO;
|
|
||||||
|
|
||||||
if (_asyncDataSourceFlags.asyncDataSourceTableViewUnlockDataSource) {
|
|
||||||
[_asyncDataSource tableViewUnlockDataSource:self];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
|
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
|
||||||
{
|
{
|
||||||
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
||||||
|
|||||||
@@ -26,17 +26,6 @@
|
|||||||
|
|
||||||
@implementation ASChangeSetDataController
|
@implementation ASChangeSetDataController
|
||||||
|
|
||||||
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
|
||||||
{
|
|
||||||
if (!(self = [super initWithAsyncDataFetching:asyncDataFetchingEnabled])) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
_changeSetBatchUpdateCounter = 0;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Batching (External API)
|
#pragma mark - Batching (External API)
|
||||||
|
|
||||||
- (void)beginUpdates
|
- (void)beginUpdates
|
||||||
@@ -66,6 +55,8 @@
|
|||||||
[super deleteSections:change.indexSet withAnimationOptions:change.animationOptions];
|
[super deleteSections:change.indexSet withAnimationOptions:change.animationOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Shouldn't reloads be processed before deletes, since deletes affect
|
||||||
|
// the index space and reloads don't?
|
||||||
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) {
|
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) {
|
||||||
[super reloadSections:change.indexSet withAnimationOptions:change.animationOptions];
|
[super reloadSections:change.indexSet withAnimationOptions:change.animationOptions];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,9 +32,9 @@
|
|||||||
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
|
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
self = [super initWithAsyncDataFetching:asyncDataFetchingEnabled];
|
self = [super init];
|
||||||
if (self != nil) {
|
if (self != nil) {
|
||||||
_pendingContexts = [NSMutableDictionary dictionary];
|
_pendingContexts = [NSMutableDictionary dictionary];
|
||||||
}
|
}
|
||||||
@@ -53,15 +53,13 @@
|
|||||||
|
|
||||||
- (void)willReloadData
|
- (void)willReloadData
|
||||||
{
|
{
|
||||||
NSArray *keys = _pendingContexts.allKeys;
|
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, __unused BOOL * _Nonnull stop) {
|
||||||
for (NSString *kind in keys) {
|
|
||||||
NSMutableArray<ASIndexedNodeContext *> *contexts = _pendingContexts[kind];
|
|
||||||
// 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 = [self indexPathsForEditingNodesOfKind:kind];
|
NSArray *indexPaths = [self indexPathsForEditingNodesOfKind:kind];
|
||||||
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
|
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
|
|
||||||
NSArray *editingNodes = [self editingNodesOfKind:kind];
|
NSArray *editingNodes = [self editingNodesOfKind:kind];
|
||||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)];
|
NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)];
|
||||||
[self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil];
|
[self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil];
|
||||||
|
|
||||||
// Insert each section
|
// Insert each section
|
||||||
@@ -75,8 +73,8 @@
|
|||||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
}];
|
}];
|
||||||
[_pendingContexts removeObjectForKey:kind];
|
}];
|
||||||
}
|
[_pendingContexts removeAllObjects];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareForInsertSections:(NSIndexSet *)sections
|
- (void)prepareForInsertSections:(NSIndexSet *)sections
|
||||||
|
|||||||
@@ -57,17 +57,6 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
|
|||||||
*/
|
*/
|
||||||
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController;
|
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController;
|
||||||
|
|
||||||
/**
|
|
||||||
Lock the data source for data fetching.
|
|
||||||
*/
|
|
||||||
- (void)dataControllerLockDataSource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Unlock the data source after data fetching.
|
|
||||||
*/
|
|
||||||
- (void)dataControllerUnlockDataSource;
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@protocol ASDataControllerEnvironmentDelegate
|
@protocol ASDataControllerEnvironmentDelegate
|
||||||
@@ -135,20 +124,6 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic, weak) id<ASDataControllerEnvironmentDelegate> environmentDelegate;
|
@property (nonatomic, weak) id<ASDataControllerEnvironmentDelegate> environmentDelegate;
|
||||||
|
|
||||||
/**
|
|
||||||
* Designated initializer.
|
|
||||||
*
|
|
||||||
* @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 handling large scale data. On another hand, the application code
|
|
||||||
* will take the responsibility to avoid data inconsistency. 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;
|
|
||||||
|
|
||||||
/** @name Data Updating */
|
/** @name Data Updating */
|
||||||
|
|
||||||
- (void)beginUpdates;
|
- (void)beginUpdates;
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking.
|
NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking.
|
||||||
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 _initialReloadDataHasBeenCalled;
|
BOOL _initialReloadDataHasBeenCalled;
|
||||||
|
|
||||||
BOOL _delegateDidInsertNodes;
|
BOOL _delegateDidInsertNodes;
|
||||||
@@ -62,7 +60,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
#pragma mark - Lifecycle
|
#pragma mark - Lifecycle
|
||||||
|
|
||||||
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if (!(self = [super init])) {
|
if (!(self = [super init])) {
|
||||||
return nil;
|
return nil;
|
||||||
@@ -83,7 +81,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
_editingTransactionQueue.name = @"org.AsyncDisplayKit.ASDataController.editingTransactionQueue";
|
_editingTransactionQueue.name = @"org.AsyncDisplayKit.ASDataController.editingTransactionQueue";
|
||||||
|
|
||||||
_batchUpdateCounter = 0;
|
_batchUpdateCounter = 0;
|
||||||
_asyncDataFetchingEnabled = asyncDataFetchingEnabled;
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@@ -119,6 +116,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
- (void)batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
|
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
|
||||||
NSUInteger count = contexts.count;
|
NSUInteger count = contexts.count;
|
||||||
|
|
||||||
@@ -143,8 +142,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
|
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
|
||||||
{
|
{
|
||||||
[node measureWithSizeRange:constrainedSize];
|
CGSize size = [node measureWithSizeRange:constrainedSize].size;
|
||||||
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
|
node.frame = { .size = size };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,6 +151,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_batchLayoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
[self batchLayoutNodesFromContexts:contexts ofKind:ASDataControllerRowNodeKind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
[self batchLayoutNodesFromContexts:contexts ofKind:ASDataControllerRowNodeKind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||||
// Insert finished nodes into data storage
|
// Insert finished nodes into data storage
|
||||||
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
@@ -163,6 +164,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_layoutNodes:(NSArray<ASCellNode *> *)nodes fromContexts:(NSArray<ASIndexedNodeContext *> *)contexts atIndexesOfRange:(NSRange)range ofKind:(NSString *)kind
|
- (void)_layoutNodes:(NSArray<ASCellNode *> *)nodes fromContexts:(NSArray<ASIndexedNodeContext *> *)contexts atIndexesOfRange:(NSRange)range ofKind:(NSString *)kind
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] != _editingTransactionQueue, @"%@ should not be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
if (_dataSource == nil) {
|
if (_dataSource == nil) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -182,6 +185,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
- (void)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
- (void)_layoutNodesFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts ofKind:(NSString *)kind completion:(ASDataControllerCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
if (!contexts.count || _dataSource == nil) {
|
if (!contexts.count || _dataSource == nil) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -278,7 +283,6 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
NSMutableArray *editingNodes = _editingNodes[kind];
|
NSMutableArray *editingNodes = _editingNodes[kind];
|
||||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes);
|
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes);
|
||||||
_editingNodes[kind] = editingNodes;
|
|
||||||
|
|
||||||
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
|
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
|
||||||
NSMutableArray *completedNodes = ASTwoDimensionalArrayDeepMutableCopy(editingNodes);
|
NSMutableArray *completedNodes = ASTwoDimensionalArrayDeepMutableCopy(editingNodes);
|
||||||
@@ -359,7 +363,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
[self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
[self insertNodes:nodes ofKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (_delegateDidInsertNodes)
|
if (_delegateDidInsertNodes)
|
||||||
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
@@ -373,7 +381,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
[self deleteNodesOfKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
[self deleteNodesOfKind:ASDataControllerRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (_delegateDidDeleteNodes)
|
if (_delegateDidDeleteNodes)
|
||||||
[_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
@@ -387,7 +399,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
[self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) {
|
[self insertSections:sections ofKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) {
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (_delegateDidInsertSections)
|
if (_delegateDidInsertSections)
|
||||||
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
@@ -401,7 +417,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"%@ must be called on the editing transaction queue", NSStringFromSelector(_cmd));
|
||||||
|
|
||||||
[self deleteSectionsOfKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) {
|
[self deleteSectionsOfKind:ASDataControllerRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) {
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
if (_delegateDidDeleteSections)
|
if (_delegateDidDeleteSections)
|
||||||
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
}];
|
}];
|
||||||
@@ -426,49 +446,44 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
||||||
|
|
||||||
[self accessDataSourceSynchronously:synchronously withBlock:^{
|
NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self];
|
||||||
NSUInteger sectionCount = [_dataSource numberOfSectionsInDataController:self];
|
NSIndexSet *sectionIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
||||||
NSIndexSet *sectionIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
|
||||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
|
|
||||||
|
|
||||||
// Allow subclasses to perform setup before going into the edit transaction
|
// Allow subclasses to perform setup before going into the edit transaction
|
||||||
[self prepareForReloadData];
|
[self prepareForReloadData];
|
||||||
|
|
||||||
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
|
LOG(@"Edit Transaction - reloadData");
|
||||||
|
|
||||||
void (^transactionBlock)() = ^{
|
// Remove everything that existed before the reload, now that we're ready to insert replacements
|
||||||
LOG(@"Edit Transaction - reloadData");
|
NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind];
|
||||||
|
NSUInteger editingNodesSectionCount = editingNodes.count;
|
||||||
// Remove everything that existed before the reload, now that we're ready to insert replacements
|
|
||||||
NSMutableArray *editingNodes = _editingNodes[ASDataControllerRowNodeKind];
|
|
||||||
NSUInteger editingNodesSectionCount = editingNodes.count;
|
|
||||||
|
|
||||||
if (editingNodesSectionCount) {
|
|
||||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodesSectionCount)];
|
|
||||||
[self _deleteNodesAtIndexPaths:ASIndexPathsForTwoDimensionalArray(editingNodes) withAnimationOptions:animationOptions];
|
|
||||||
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self willReloadData];
|
|
||||||
|
|
||||||
// Insert empty sections
|
|
||||||
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
|
|
||||||
for (int i = 0; i < sectionCount; i++) {
|
|
||||||
[sections addObject:[[NSMutableArray alloc] init]];
|
|
||||||
}
|
|
||||||
[self _insertSections:sections atIndexSet:sectionIndexSet withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
if (completion) {
|
|
||||||
dispatch_async(dispatch_get_main_queue(), completion);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (synchronously) {
|
if (editingNodesSectionCount) {
|
||||||
transactionBlock();
|
NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodesSectionCount)];
|
||||||
} else {
|
[self _deleteNodesAtIndexPaths:ASIndexPathsForTwoDimensionalArray(editingNodes) withAnimationOptions:animationOptions];
|
||||||
[_editingTransactionQueue addOperationWithBlock:transactionBlock];
|
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self willReloadData];
|
||||||
|
|
||||||
|
// Insert empty sections
|
||||||
|
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
[sections addObject:[[NSMutableArray alloc] init]];
|
||||||
|
}
|
||||||
|
[self _insertSections:sections atIndexSet:sectionIndexSet withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
|
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
|
if (completion) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), completion);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
if (synchronously) {
|
||||||
|
[self waitUntilAllUpdatesAreCommitted];
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,45 +506,21 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
#pragma mark - Data Source Access (Calling _dataSource)
|
#pragma mark - Data Source Access (Calling _dataSource)
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely locks access to the data source and executes the given block, unlocking once complete.
|
|
||||||
*
|
|
||||||
* @discussion When `asyncDataFetching` is enabled, the block is executed on a background thread.
|
|
||||||
*/
|
|
||||||
- (void)accessDataSourceWithBlock:(dispatch_block_t)block
|
|
||||||
{
|
|
||||||
[self accessDataSourceSynchronously:NO withBlock:block];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)accessDataSourceSynchronously:(BOOL)synchronously withBlock:(dispatch_block_t)block
|
|
||||||
{
|
|
||||||
if (!synchronously && _asyncDataFetchingEnabled) {
|
|
||||||
[_dataSource dataControllerLockDataSource];
|
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
|
||||||
block();
|
|
||||||
[_dataSource dataControllerUnlockDataSource];
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
[_dataSource dataControllerLockDataSource];
|
|
||||||
block();
|
|
||||||
[_dataSource dataControllerUnlockDataSource];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches row contexts for the provided sections from the data source.
|
* Fetches row contexts for the provided sections from the data source.
|
||||||
*/
|
*/
|
||||||
- (NSArray<ASIndexedNodeContext *> *)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet
|
- (NSArray<ASIndexedNodeContext *> *)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet
|
||||||
{
|
{
|
||||||
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||||
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
||||||
|
|
||||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||||
[indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
[indexSet enumerateIndexesUsingBlock:^(NSUInteger sectionIndex, BOOL *stop) {
|
||||||
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:idx];
|
NSUInteger rowNum = [_dataSource dataController:self rowsInSection:sectionIndex];
|
||||||
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
|
|
||||||
for (NSUInteger i = 0; i < rowNum; i++) {
|
for (NSUInteger i = 0; i < rowNum; i++) {
|
||||||
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sectionIndex];
|
||||||
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
||||||
|
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
||||||
@@ -628,24 +619,22 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
LOG(@"Edit Command - insertSections: %@", sections);
|
LOG(@"Edit Command - insertSections: %@", sections);
|
||||||
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
||||||
|
|
||||||
[self accessDataSourceWithBlock:^{
|
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sections];
|
||||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sections];
|
|
||||||
|
|
||||||
[self prepareForInsertSections:sections];
|
[self prepareForInsertSections:sections];
|
||||||
|
|
||||||
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
|
[self willInsertSections:sections];
|
||||||
|
|
||||||
|
LOG(@"Edit Transaction - insertSections: %@", sections);
|
||||||
|
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count];
|
||||||
|
for (NSUInteger i = 0; i < sections.count; i++) {
|
||||||
|
[sectionArray addObject:[NSMutableArray array]];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
[self willInsertSections:sections];
|
|
||||||
|
|
||||||
LOG(@"Edit Transaction - insertSections: %@", sections);
|
|
||||||
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count];
|
|
||||||
for (NSUInteger i = 0; i < sections.count; i++) {
|
|
||||||
[sectionArray addObject:[NSMutableArray array]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self _insertSections:sectionArray atIndexSet:sections withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
|
||||||
}];
|
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -678,23 +667,21 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
||||||
|
|
||||||
[self accessDataSourceWithBlock:^{
|
NSArray<ASIndexedNodeContext *> *contexts= [self _populateFromDataSourceWithSectionIndexSet:sections];
|
||||||
NSArray<ASIndexedNodeContext *> *contexts= [self _populateFromDataSourceWithSectionIndexSet:sections];
|
|
||||||
|
|
||||||
[self prepareForReloadSections:sections];
|
[self prepareForReloadSections:sections];
|
||||||
|
|
||||||
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
|
[self willReloadSections:sections];
|
||||||
|
|
||||||
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections);
|
||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForTwoDimensionalArray(_editingNodes[ASDataControllerRowNodeKind]));
|
||||||
[self willReloadSections:sections];
|
|
||||||
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASDataControllerRowNodeKind], sections);
|
// reinsert the elements
|
||||||
|
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
LOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForTwoDimensionalArray(_editingNodes[ASDataControllerRowNodeKind]));
|
|
||||||
|
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
|
||||||
|
|
||||||
// reinsert the elements
|
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
|
||||||
}];
|
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -818,27 +805,25 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
|
|
||||||
[self accessDataSourceWithBlock:^{
|
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
||||||
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
|
||||||
|
for (NSIndexPath *indexPath in sortedIndexPaths) {
|
||||||
for (NSIndexPath *indexPath in sortedIndexPaths) {
|
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
||||||
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
||||||
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
indexPath:indexPath
|
||||||
indexPath:indexPath
|
constrainedSize:constrainedSize
|
||||||
constrainedSize:constrainedSize
|
environmentTraitCollection:environmentTraitCollection]];
|
||||||
environmentTraitCollection:environmentTraitCollection]];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[self prepareForInsertRowsAtIndexPaths:indexPaths];
|
[self prepareForInsertRowsAtIndexPaths:indexPaths];
|
||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
[self willInsertRowsAtIndexPaths:indexPaths];
|
[self willInsertRowsAtIndexPaths:indexPaths];
|
||||||
|
|
||||||
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
|
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
}];
|
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -874,35 +859,32 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
[_editingTransactionQueue waitUntilAllOperationsAreFinished];
|
||||||
|
|
||||||
// Reloading requires re-fetching the data. Load it on the current calling thread, locking the data source.
|
NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
||||||
[self accessDataSourceWithBlock:^{
|
|
||||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
|
// Sort indexPath to avoid messing up the index when deleting
|
||||||
|
// FIXME: Shouldn't deletes be sorted in descending order?
|
||||||
// Sort indexPath to avoid messing up the index when deleting
|
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||||
// FIXME: Shouldn't deletes be sorted in descending order?
|
|
||||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||||
|
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
||||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
|
||||||
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
for (NSIndexPath *indexPath in sortedIndexPaths) {
|
||||||
|
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
||||||
for (NSIndexPath *indexPath in sortedIndexPaths) {
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
||||||
ASCellNodeBlock nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath];
|
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
indexPath:indexPath
|
||||||
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
constrainedSize:constrainedSize
|
||||||
indexPath:indexPath
|
environmentTraitCollection:environmentTraitCollection]];
|
||||||
constrainedSize:constrainedSize
|
}
|
||||||
environmentTraitCollection:environmentTraitCollection]];
|
|
||||||
}
|
|
||||||
|
|
||||||
[self prepareForReloadRowsAtIndexPaths:indexPaths];
|
[self prepareForReloadRowsAtIndexPaths:indexPaths];
|
||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
[self willReloadRowsAtIndexPaths:indexPaths];
|
[self willReloadRowsAtIndexPaths:indexPaths];
|
||||||
|
|
||||||
LOG(@"Edit Transaction - reloadRows: %@", indexPaths);
|
LOG(@"Edit Transaction - reloadRows: %@", indexPaths);
|
||||||
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
|
||||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||||
}];
|
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
@@ -935,16 +917,18 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self accessDataSourceWithBlock:^{
|
NSUInteger sectionIndex = 0;
|
||||||
[nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) {
|
for (NSMutableArray *section in nodes) {
|
||||||
[section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) {
|
NSUInteger rowIndex = 0;
|
||||||
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
|
for (ASCellNode *node in section) {
|
||||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
|
||||||
ASLayout *layout = [node measureWithSizeRange:constrainedSize];
|
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||||
node.frame = CGRectMake(0.0f, 0.0f, layout.size.width, layout.size.height);
|
CGSize size = [node measureWithSizeRange:constrainedSize].size;
|
||||||
}];
|
node.frame = { .size = size };
|
||||||
}];
|
rowIndex += 1;
|
||||||
}];
|
}
|
||||||
|
sectionIndex += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
@@ -1021,17 +1005,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
|
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
|
|
||||||
NSArray *nodes = [self completedNodes];
|
|
||||||
NSUInteger numberOfNodes = nodes.count;
|
|
||||||
|
|
||||||
|
NSInteger section = 0;
|
||||||
// Loop through each section to look for the cellNode
|
// Loop through each section to look for the cellNode
|
||||||
for (NSUInteger i = 0; i < numberOfNodes; i++) {
|
for (NSArray *sectionNodes in [self completedNodes]) {
|
||||||
NSArray *sectionNodes = nodes[i];
|
NSUInteger item = [sectionNodes indexOfObjectIdenticalTo:cellNode];
|
||||||
NSUInteger cellIndex = [sectionNodes indexOfObjectIdenticalTo:cellNode];
|
if (item != NSNotFound) {
|
||||||
if (cellIndex != NSNotFound) {
|
return [NSIndexPath indexPathForItem:item inSection:section];
|
||||||
return [NSIndexPath indexPathForRow:cellIndex inSection:i];
|
|
||||||
}
|
}
|
||||||
|
section += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
|
|||||||
@@ -11,6 +11,10 @@
|
|||||||
#import "ASAssert.h"
|
#import "ASAssert.h"
|
||||||
#import "ASMultidimensionalArrayUtils.h"
|
#import "ASMultidimensionalArrayUtils.h"
|
||||||
|
|
||||||
|
// Import UIKit to get [NSIndexPath indexPathForItem:inSection:] which uses
|
||||||
|
// static memory addresses rather than allocating new index path objects.
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
#pragma mark - Internal Methods
|
#pragma mark - Internal Methods
|
||||||
|
|
||||||
static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray,
|
static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray *mutableArray,
|
||||||
@@ -25,8 +29,10 @@ static void ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(NSMutableArray
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (curIndexPath.length < dimension - 1) {
|
if (curIndexPath.length < dimension - 1) {
|
||||||
for (int i = 0; i < mutableArray.count; i++) {
|
NSInteger i = 0;
|
||||||
ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(mutableArray[i], indexPaths, curIdx, [curIndexPath indexPathByAddingIndex:i], dimension, updateBlock);
|
for (NSMutableArray *subarray in mutableArray) {
|
||||||
|
ASRecursivelyUpdateMultidimensionalArrayAtIndexPaths(subarray, indexPaths, curIdx, [curIndexPath indexPathByAddingIndex:i], dimension, updateBlock);
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
|
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
|
||||||
@@ -72,7 +78,12 @@ static BOOL ASElementExistsAtIndexPathForMultidimensionalArray(NSArray *array, N
|
|||||||
NSUInteger indexesLength = indexLength - 1;
|
NSUInteger indexesLength = indexLength - 1;
|
||||||
NSUInteger indexes[indexesLength];
|
NSUInteger indexes[indexesLength];
|
||||||
[indexPath getIndexes:indexes range:NSMakeRange(1, indexesLength)];
|
[indexPath getIndexes:indexes range:NSMakeRange(1, indexesLength)];
|
||||||
NSIndexPath *indexPathByRemovingFirstIndex = [NSIndexPath indexPathWithIndexes:indexes length:indexesLength];
|
NSIndexPath *indexPathByRemovingFirstIndex;
|
||||||
|
if (indexesLength == 2) {
|
||||||
|
indexPathByRemovingFirstIndex = [NSIndexPath indexPathForItem:indexes[1] inSection:indexes[0]];
|
||||||
|
} else {
|
||||||
|
indexPathByRemovingFirstIndex = [NSIndexPath indexPathWithIndexes:indexes length:indexesLength];
|
||||||
|
}
|
||||||
|
|
||||||
return ASElementExistsAtIndexPathForMultidimensionalArray(array[firstIndex], indexPathByRemovingFirstIndex);
|
return ASElementExistsAtIndexPathForMultidimensionalArray(array[firstIndex], indexPathByRemovingFirstIndex);
|
||||||
}
|
}
|
||||||
@@ -184,9 +195,8 @@ NSArray *ASIndexPathsForTwoDimensionalArray(NSArray <NSArray *>* twoDimensionalA
|
|||||||
NSUInteger section = 0;
|
NSUInteger section = 0;
|
||||||
for (NSArray *subarray in twoDimensionalArray) {
|
for (NSArray *subarray in twoDimensionalArray) {
|
||||||
ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray<NSArray *> *");
|
ASDisplayNodeCAssert([subarray isKindOfClass:[NSArray class]], @"This function expects NSArray<NSArray *> *");
|
||||||
NSUInteger itemCount = subarray.count;
|
for (NSUInteger item = 0; item < subarray.count; item++) {
|
||||||
for (NSUInteger item = 0; item < itemCount; item++) {
|
[result addObject:[NSIndexPath indexPathForItem:item inSection:section]];
|
||||||
[result addObject:[NSIndexPath indexPathWithIndexes:(const NSUInteger []){ section, item } length:2]];
|
|
||||||
}
|
}
|
||||||
section++;
|
section++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -408,8 +408,7 @@
|
|||||||
{
|
{
|
||||||
CGSize tableViewSize = CGSizeMake(100, 500);
|
CGSize tableViewSize = CGSizeMake(100, 500);
|
||||||
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height)
|
ASTestTableView *tableView = [[ASTestTableView alloc] initWithFrame:CGRectMake(0, 0, tableViewSize.width, tableViewSize.height)
|
||||||
style:UITableViewStylePlain
|
style:UITableViewStylePlain];
|
||||||
asyncDataFetching:YES];
|
|
||||||
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new];
|
||||||
|
|
||||||
tableView.asyncDelegate = dataSource;
|
tableView.asyncDelegate = dataSource;
|
||||||
|
|||||||
Reference in New Issue
Block a user