mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Clean up interfacing with internal editing/completed stores
This commit is contained in:
committed by
Levi McCallum
parent
835f9e99ca
commit
afda471bd0
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#import "ASCollectionDataController.h"
|
#import "ASCollectionDataController.h"
|
||||||
|
|
||||||
|
#import "ASLog.h"
|
||||||
#import "ASAssert.h"
|
#import "ASAssert.h"
|
||||||
#import "ASMultidimensionalArrayUtils.h"
|
#import "ASMultidimensionalArrayUtils.h"
|
||||||
#import "ASDisplayNode.h"
|
#import "ASDisplayNode.h"
|
||||||
@@ -21,17 +22,18 @@
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation ASCollectionDataController {
|
@implementation ASCollectionDataController {
|
||||||
NSMutableDictionary *_completedSupplementaryNodes;
|
|
||||||
NSMutableDictionary *_editingSupplementaryNodes;
|
|
||||||
|
|
||||||
NSMutableDictionary *_pendingNodes;
|
NSMutableDictionary *_pendingNodes;
|
||||||
NSMutableDictionary *_pendingIndexPaths;
|
NSMutableDictionary *_pendingIndexPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareForReloadData
|
- (void)prepareForReloadData
|
||||||
{
|
{
|
||||||
|
_pendingNodes = [NSMutableDictionary dictionary];
|
||||||
|
_pendingIndexPaths = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self];
|
NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self];
|
||||||
[elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) {
|
[elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||||
|
ASLOG(@"Populating elements of kind: %@", kind);
|
||||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||||
NSMutableArray *nodes = [NSMutableArray array];
|
NSMutableArray *nodes = [NSMutableArray array];
|
||||||
[self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths];
|
[self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths];
|
||||||
@@ -43,9 +45,21 @@
|
|||||||
- (void)willReloadData
|
- (void)willReloadData
|
||||||
{
|
{
|
||||||
[_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) {
|
[_pendingNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL *stop) {
|
||||||
|
ASLOG(@"Batch layout nodes of kind: %@, (%@)", kind, nodes);
|
||||||
|
|
||||||
|
// Insert each section
|
||||||
|
NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind];
|
||||||
|
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
[sections addObject:[[NSMutableArray alloc] init]];
|
||||||
|
}
|
||||||
|
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil];
|
||||||
|
|
||||||
[self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) {
|
[self batchLayoutNodes:nodes atIndexPaths:_pendingIndexPaths[kind] constrainedSize:^ASSizeRange(NSIndexPath *indexPath) {
|
||||||
return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
|
return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
|
||||||
} completion:nil];
|
} completion:^(NSArray *nodes, NSArray *indexPaths) {
|
||||||
|
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||||
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +80,7 @@
|
|||||||
- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
- (ASDisplayNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
return _completedSupplementaryNodes[kind][indexPath.section][indexPath.item];
|
return [self internalCompletedNodes][kind][indexPath.section][indexPath.item];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id<ASCollectionDataControllerSource>)collectionDataSource
|
- (id<ASCollectionDataControllerSource>)collectionDataSource
|
||||||
@@ -74,37 +88,4 @@
|
|||||||
return (id<ASCollectionDataControllerSource>)self.dataSource;
|
return (id<ASCollectionDataControllerSource>)self.dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Internal Data Querying
|
|
||||||
|
|
||||||
// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController
|
|
||||||
- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths
|
|
||||||
{
|
|
||||||
if (indexPaths.count == 0)
|
|
||||||
return;
|
|
||||||
NSMutableArray *editingNodes = [self _editingNodesOfKind:kind];
|
|
||||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths, nodes);
|
|
||||||
NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes);
|
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
|
||||||
_completedSupplementaryNodes[kind] = completedNodes;
|
|
||||||
// TODO: Notify change
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController
|
|
||||||
- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths
|
|
||||||
{
|
|
||||||
if (indexPaths.count == 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMutableArray *)_completedNodesOfKind:(NSString *)kind
|
|
||||||
{
|
|
||||||
return _completedSupplementaryNodes[kind];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMutableArray *)_editingNodesOfKind:(NSString *)kind
|
|
||||||
{
|
|
||||||
return _editingSupplementaryNodes[kind];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -42,7 +42,11 @@
|
|||||||
|
|
||||||
- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind
|
- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryKind:(NSString *)kind
|
||||||
{
|
{
|
||||||
return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView];
|
if ([collectionView.asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]) {
|
||||||
|
return [collectionView.asyncDataSource numberOfSectionsInCollectionView:collectionView];
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section
|
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section
|
||||||
|
|||||||
@@ -25,11 +25,6 @@
|
|||||||
*/
|
*/
|
||||||
- (void)accessDataSourceWithBlock:(dispatch_block_t)block;
|
- (void)accessDataSourceWithBlock:(dispatch_block_t)block;
|
||||||
|
|
||||||
/**
|
|
||||||
* Measure and layout the given nodes in optimized batches, constraining each to a given size.
|
|
||||||
*/
|
|
||||||
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An opportunity for a subclass to access the data source before entering into the editing queue
|
* An opportunity for a subclass to access the data source before entering into the editing queue
|
||||||
*/
|
*/
|
||||||
@@ -40,4 +35,19 @@
|
|||||||
*/
|
*/
|
||||||
- (void)willReloadData;
|
- (void)willReloadData;
|
||||||
|
|
||||||
|
- (NSMutableDictionary *)internalCompletedNodes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measure and layout the given nodes in optimized batches, constraining each to a given size.
|
||||||
|
*/
|
||||||
|
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
|
||||||
|
|
||||||
|
- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
|
||||||
|
|
||||||
|
- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
|
||||||
|
|
||||||
|
- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock;
|
||||||
|
|
||||||
|
- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,12 +19,16 @@
|
|||||||
|
|
||||||
const static NSUInteger kASDataControllerSizingCountPerProcessor = 5;
|
const static NSUInteger kASDataControllerSizingCountPerProcessor = 5;
|
||||||
|
|
||||||
|
NSString * const ASRowNodeKind = @"_ASRowNodeKind";
|
||||||
|
|
||||||
static void *kASSizingQueueContext = &kASSizingQueueContext;
|
static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||||
|
|
||||||
@interface ASDataController () {
|
@interface ASDataController () {
|
||||||
NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available.
|
NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available.
|
||||||
NSMutableArray *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
|
NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
|
||||||
NSMutableArray *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes.
|
NSMutableDictionary *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
@@ -50,9 +54,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
_completedNodes = [NSMutableArray array];
|
_completedNodes = [NSMutableDictionary dictionary];
|
||||||
_editingNodes = [NSMutableArray array];
|
_editingNodes = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
_completedNodes[ASRowNodeKind] = [NSMutableArray array];
|
||||||
|
_editingNodes[ASRowNodeKind] = [NSMutableArray array];
|
||||||
|
|
||||||
_pendingEditCommandBlocks = [NSMutableArray array];
|
_pendingEditCommandBlocks = [NSMutableArray array];
|
||||||
|
|
||||||
_editingTransactionQueue = [[NSOperationQueue alloc] init];
|
_editingTransactionQueue = [[NSOperationQueue alloc] init];
|
||||||
@@ -178,7 +185,78 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
|
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
|
||||||
free(nodeBoundSizes);
|
free(nodeBoundSizes);
|
||||||
|
|
||||||
completionBlock(nodes, indexPaths);
|
if (completionBlock)
|
||||||
|
completionBlock(nodes, indexPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - External Data Querying + Editing
|
||||||
|
|
||||||
|
- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
|
||||||
|
{
|
||||||
|
if (indexPaths.count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
NSMutableArray *editingNodes = _editingNodes[kind];
|
||||||
|
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.
|
||||||
|
NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes);
|
||||||
|
|
||||||
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
|
_completedNodes[kind] = completedNodes;
|
||||||
|
if (completionBlock) {
|
||||||
|
completionBlock(nodes, indexPaths);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
|
||||||
|
{
|
||||||
|
if (indexPaths.count == 0)
|
||||||
|
return;
|
||||||
|
ASLOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind]));
|
||||||
|
NSMutableArray *editingNodes = _editingNodes[kind];
|
||||||
|
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
|
||||||
|
_editingNodes[kind] = editingNodes;
|
||||||
|
|
||||||
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths);
|
||||||
|
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths);
|
||||||
|
if (completionBlock) {
|
||||||
|
completionBlock(nodes, indexPaths);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock
|
||||||
|
{
|
||||||
|
if (indexSet.count == 0)
|
||||||
|
return;
|
||||||
|
[_editingNodes[kind] insertObjects:sections atIndexes:indexSet];
|
||||||
|
|
||||||
|
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
|
||||||
|
NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections);
|
||||||
|
|
||||||
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
|
[_completedNodes[kind] insertObjects:sectionsForCompleted atIndexes:indexSet];
|
||||||
|
if (completionBlock) {
|
||||||
|
completionBlock(sections, indexSet);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock
|
||||||
|
{
|
||||||
|
if (indexSet.count == 0)
|
||||||
|
return;
|
||||||
|
[_editingNodes[kind] removeObjectsAtIndexes:indexSet];
|
||||||
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
|
[_completedNodes[kind] removeObjectsAtIndexes:indexSet];
|
||||||
|
if (completionBlock) {
|
||||||
|
completionBlock(indexSet);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Internal Data Querying + Editing
|
#pragma mark - Internal Data Querying + Editing
|
||||||
@@ -191,18 +269,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_insertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
if (indexPaths.count == 0)
|
[self insertNodes:nodes ofKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
||||||
return;
|
|
||||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths, nodes);
|
|
||||||
|
|
||||||
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
|
|
||||||
NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_editingNodes);
|
|
||||||
|
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
|
||||||
_completedNodes = completedNodes;
|
|
||||||
if (_delegateDidInsertNodes)
|
if (_delegateDidInsertNodes)
|
||||||
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate dataController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,17 +283,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_deleteNodesAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
if (indexPaths.count == 0)
|
[self deleteNodesOfKind:ASRowNodeKind atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
|
||||||
return;
|
|
||||||
ASLOG(@"_deleteNodesAtIndexPaths:%@, full index paths in _editingNodes = %@", indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes));
|
|
||||||
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths);
|
|
||||||
|
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths);
|
|
||||||
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes, indexPaths);
|
|
||||||
if (_delegateDidDeleteNodes)
|
if (_delegateDidDeleteNodes)
|
||||||
[_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[_delegate dataController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
});
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -234,18 +297,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_insertSections:(NSMutableArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
if (indexSet.count == 0)
|
[self insertSections:sections ofKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSArray *sections, NSIndexSet *indexSet) {
|
||||||
return;
|
|
||||||
[_editingNodes insertObjects:sections atIndexes:indexSet];
|
|
||||||
|
|
||||||
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
|
|
||||||
NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections);
|
|
||||||
|
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
|
||||||
[_completedNodes insertObjects:sectionsForCompleted atIndexes:indexSet];
|
|
||||||
if (_delegateDidInsertSections)
|
if (_delegateDidInsertSections)
|
||||||
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate dataController:self didInsertSections:sections atIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -256,14 +311,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
*/
|
*/
|
||||||
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
- (void)_deleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||||
{
|
{
|
||||||
if (indexSet.count == 0)
|
[self deleteSectionsOfKind:ASRowNodeKind atIndexSet:indexSet completion:^(NSIndexSet *indexSet) {
|
||||||
return;
|
|
||||||
[_editingNodes removeObjectsAtIndexes:indexSet];
|
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
|
||||||
[_completedNodes removeObjectsAtIndexes:indexSet];
|
|
||||||
if (_delegateDidDeleteSections)
|
if (_delegateDidDeleteSections)
|
||||||
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[_delegate dataController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
});
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Initial Load & Full Reload (External API)
|
#pragma mark - Initial Load & Full Reload (External API)
|
||||||
@@ -316,7 +367,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
ASLOG(@"Edit Transaction - reloadData");
|
ASLOG(@"Edit Transaction - reloadData");
|
||||||
|
|
||||||
// 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 = ASIndexPathsForMultidimensionalArray(_editingNodes);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]);
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)];
|
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, _editingNodes.count)];
|
||||||
@@ -435,7 +486,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
// Deep copy _completedNodes to _externalCompletedNodes.
|
// Deep copy _completedNodes to _externalCompletedNodes.
|
||||||
// Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate.
|
// Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate.
|
||||||
_externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes);
|
_externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASRowNodeKind]);
|
||||||
|
|
||||||
ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate");
|
ASLOG(@"endUpdatesWithCompletion - begin updates call to delegate");
|
||||||
[_delegate dataControllerBeginUpdates:self];
|
[_delegate dataControllerBeginUpdates:self];
|
||||||
@@ -515,7 +566,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
// remove elements
|
// remove elements
|
||||||
ASLOG(@"Edit Transaction - deleteSections: %@", indexSet);
|
ASLOG(@"Edit Transaction - deleteSections: %@", indexSet);
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, indexSet);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], indexSet);
|
||||||
|
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
|
||||||
@@ -544,9 +595,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
[self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths];
|
[self _layoutNodesWithMainThreadAffinity:updatedNodes atIndexPaths:updatedIndexPaths];
|
||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, sections);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], sections);
|
||||||
|
|
||||||
ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes));
|
ASLOG(@"Edit Transaction - reloadSections: updatedIndexPaths: %@, indexPaths: %@, _editingNodes: %@", updatedIndexPaths, indexPaths, ASIndexPathsForMultidimensionalArray(_editingNodes[ASRowNodeKind]));
|
||||||
|
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
@@ -570,8 +621,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
ASLOG(@"Edit Transaction - moveSection");
|
ASLOG(@"Edit Transaction - moveSection");
|
||||||
|
|
||||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes, [NSIndexSet indexSetWithIndex:section]);
|
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(_editingNodes[ASRowNodeKind], [NSIndexSet indexSetWithIndex:section]);
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, indexPaths);
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], indexPaths);
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
// update the section of indexpaths
|
// update the section of indexpaths
|
||||||
@@ -696,7 +747,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
// (see _layoutNodes:atIndexPaths:withAnimationOptions:).
|
// (see _layoutNodes:atIndexPaths:withAnimationOptions:).
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
ASDisplayNodePerformBlockOnMainThread(^{
|
ASDisplayNodePerformBlockOnMainThread(^{
|
||||||
relayoutNodesBlock(_completedNodes);
|
relayoutNodesBlock(_completedNodes[ASRowNodeKind]);
|
||||||
});
|
});
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
@@ -711,7 +762,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
|
|
||||||
[_editingTransactionQueue addOperationWithBlock:^{
|
[_editingTransactionQueue addOperationWithBlock:^{
|
||||||
ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath);
|
ASLOG(@"Edit Transaction - moveRow: %@ > %@", indexPath, newIndexPath);
|
||||||
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes, [NSArray arrayWithObject:indexPath]);
|
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_editingNodes[ASRowNodeKind], [NSArray arrayWithObject:indexPath]);
|
||||||
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
|
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
|
||||||
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
[self _deleteNodesAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
|
||||||
|
|
||||||
@@ -722,6 +773,13 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Data Querying (Subclass API)
|
||||||
|
|
||||||
|
- (NSMutableDictionary *)internalCompletedNodes
|
||||||
|
{
|
||||||
|
return _completedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Data Querying (External API)
|
#pragma mark - Data Querying (External API)
|
||||||
|
|
||||||
- (NSUInteger)numberOfSections
|
- (NSUInteger)numberOfSections
|
||||||
@@ -771,7 +829,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
- (NSArray *)completedNodes
|
- (NSArray *)completedNodes
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes;
|
return _externalCompletedNodes != nil ? _externalCompletedNodes : _completedNodes[ASRowNodeKind];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Dealloc
|
#pragma mark - Dealloc
|
||||||
@@ -779,15 +837,17 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
|||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
ASDisplayNodeAssertMainThread();
|
ASDisplayNodeAssertMainThread();
|
||||||
[_completedNodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) {
|
[_completedNodes enumerateKeysAndObjectsUsingBlock:^(NSString *kind, NSMutableArray *nodes, BOOL * _Nonnull stop) {
|
||||||
[section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) {
|
[nodes enumerateObjectsUsingBlock:^(NSMutableArray *section, NSUInteger sectionIndex, BOOL *stop) {
|
||||||
if (node.isNodeLoaded) {
|
[section enumerateObjectsUsingBlock:^(ASDisplayNode *node, NSUInteger rowIndex, BOOL *stop) {
|
||||||
if (node.layerBacked) {
|
if (node.isNodeLoaded) {
|
||||||
[node.layer removeFromSuperlayer];
|
if (node.layerBacked) {
|
||||||
} else {
|
[node.layer removeFromSuperlayer];
|
||||||
[node.view removeFromSuperview];
|
} else {
|
||||||
|
[node.view removeFromSuperview];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}];
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,9 +158,9 @@
|
|||||||
return rangeType == ASLayoutRangeTypeRender;
|
return rangeType == ASLayoutRangeTypeRender;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)cellNode
|
- (void)configureContentView:(UIView *)contentView forNode:(ASDisplayNode *)node
|
||||||
{
|
{
|
||||||
if (cellNode.view.superview == contentView) {
|
if (node.view.superview == contentView) {
|
||||||
// this content view is already correctly configured
|
// this content view is already correctly configured
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
[view removeFromSuperview];
|
[view removeFromSuperview];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self moveNode:cellNode toView:contentView];
|
[self moveNode:node toView:contentView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||||
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||||
|
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||||
|
|
||||||
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES];
|
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout asyncDataFetching:YES];
|
||||||
_collectionView.asyncDataSource = self;
|
_collectionView.asyncDataSource = self;
|
||||||
|
|||||||
Reference in New Issue
Block a user