mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-04-19 23:01:22 +08:00
Merge pull request #1629 from erichoracek/recalculate-supplementaries-on-item-changes
[ASCollectionView] Repopulate supplementary views on item-level changes
This commit is contained in:
@@ -156,6 +156,61 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareForInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
LOG(@"Populating elements of kind: %@, for index paths: %@", kind, indexPaths);
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts];
|
||||
_pendingContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
NSArray *keys = _pendingContexts.allKeys;
|
||||
for (NSString *kind in keys) {
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = _pendingContexts[kind];
|
||||
|
||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
}];
|
||||
[_pendingContexts removeObjectForKey:kind];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSArray<NSIndexPath *> *deletedIndexPaths = ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths([self editingNodesOfKind:kind], indexPaths);
|
||||
[self deleteNodesOfKind:kind atIndexPaths:deletedIndexPaths completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareForReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts];
|
||||
_pendingContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
NSArray *keys = _pendingContexts.allKeys;
|
||||
for (NSString *kind in keys) {
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = _pendingContexts[kind];
|
||||
|
||||
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
// reinsert the elements
|
||||
[self batchLayoutNodesFromContexts:contexts ofKind:kind completion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
}];
|
||||
[_pendingContexts removeObjectForKey:kind];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
|
||||
{
|
||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||
@@ -167,21 +222,7 @@
|
||||
NSUInteger rowCount = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:i];
|
||||
for (NSUInteger j = 0; j < rowCount; j++) {
|
||||
NSIndexPath *indexPath = [sectionIndexPath indexPathByAddingIndex:j];
|
||||
|
||||
ASCellNodeBlock supplementaryCellBlock;
|
||||
if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) {
|
||||
supplementaryCellBlock = [self.collectionDataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
|
||||
} else {
|
||||
ASCellNode *supplementaryNode = [self.collectionDataSource dataController:self supplementaryNodeOfKind:kind atIndexPath:indexPath];
|
||||
supplementaryCellBlock = ^{ return supplementaryNode; };
|
||||
}
|
||||
|
||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||
ASIndexedNodeContext *context = [[ASIndexedNodeContext alloc] initWithNodeBlock:supplementaryCellBlock
|
||||
indexPath:indexPath
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection];
|
||||
[contexts addObject:context];
|
||||
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,7 +237,33 @@
|
||||
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
|
||||
for (NSUInteger i = 0; i < rowNum; i++) {
|
||||
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
|
||||
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind atIndexPaths:(NSArray<NSIndexPath *> *)indexPaths mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
|
||||
{
|
||||
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
|
||||
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
|
||||
|
||||
NSMutableIndexSet *sections = [NSMutableIndexSet indexSet];
|
||||
for (NSIndexPath *indexPath in indexPaths) {
|
||||
[sections addIndex:indexPath.section];
|
||||
}
|
||||
|
||||
[sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
|
||||
NSUInteger rowNum = [self.collectionDataSource dataController:self supplementaryNodesOfKind:kind inSection:idx];
|
||||
NSIndexPath *sectionIndex = [[NSIndexPath alloc] initWithIndex:idx];
|
||||
for (NSUInteger i = 0; i < rowNum; i++) {
|
||||
NSIndexPath *indexPath = [sectionIndex indexPathByAddingIndex:i];
|
||||
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)_populateSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection
|
||||
{
|
||||
ASCellNodeBlock supplementaryCellBlock;
|
||||
if (_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath) {
|
||||
supplementaryCellBlock = [self.collectionDataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
|
||||
@@ -211,8 +278,6 @@
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection];
|
||||
[contexts addObject:context];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Sizing query
|
||||
@@ -264,4 +329,4 @@
|
||||
ASDisplayNodeAssertTrue(_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath || [self.collectionDataSource respondsToSelector:@selector(dataController:supplementaryNodeOfKind:atIndexPath:)]);
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -160,4 +160,70 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCellNode *> *nodes, NS
|
||||
*/
|
||||
- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Notifies the subclass to perform setup before rows are inserted in the data controller.
|
||||
*
|
||||
* @discussion This method will be performed before the data controller enters its editing queue.
|
||||
* The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
|
||||
* data stores before entering into editing the backing store on a background thread.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be inserted.
|
||||
*/
|
||||
- (void)prepareForInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Notifies the subclass that the data controller will insert new rows at the given index paths.
|
||||
*
|
||||
* @discussion This method will be performed on the data controller's editing background queue before the parent's
|
||||
* concrete implementation. This is a great place to perform any additional transformations like supplementary views
|
||||
* or header/footer nodes.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be inserted.
|
||||
*/
|
||||
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Notifies the subclass to perform setup before rows are deleted in the data controller.
|
||||
*
|
||||
* @discussion This method will be performed before the data controller enters its editing queue.
|
||||
* The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
|
||||
* data stores before entering into editing the backing store on a background thread.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be deleted.
|
||||
*/
|
||||
- (void)prepareForDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Notifies the subclass that the data controller will delete rows at the given index paths.
|
||||
*
|
||||
* @discussion This method will be performed before the data controller enters its editing queue.
|
||||
* The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
|
||||
* data stores before entering into editing the backing store on a background thread.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be deleted.
|
||||
*/
|
||||
- (void)willDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Notifies the subclass to perform any work needed before the given rows will be reloaded.
|
||||
*
|
||||
* @discussion This method will be performed before the data controller enters its editing queue, usually on the main
|
||||
* thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
|
||||
* data stores before entering into editing the backing store on a background thread.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be reloaded.
|
||||
*/
|
||||
- (void)prepareForReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Notifies the subclass that the data controller will reload the rows at the given index paths.
|
||||
*
|
||||
* @discussion This method will be performed on the data controller's editing background queue before the parent's
|
||||
* concrete implementation. This is a great place to perform any additional transformations like supplementary views
|
||||
* or header/footer nodes.
|
||||
*
|
||||
* @param indexPaths Index paths for the rows to be reloaded.
|
||||
*/
|
||||
- (void)willReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
@end
|
||||
|
||||
@@ -772,6 +772,36 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)prepareForInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)prepareForDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)willDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)prepareForReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
- (void)willReloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
|
||||
}
|
||||
|
||||
#pragma mark - Row Editing (External API)
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
@@ -799,7 +829,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
environmentTraitCollection:environmentTraitCollection]];
|
||||
}
|
||||
|
||||
[self prepareForInsertRowsAtIndexPaths:indexPaths];
|
||||
|
||||
[_editingTransactionQueue addOperationWithBlock:^{
|
||||
[self willInsertRowsAtIndexPaths:indexPaths];
|
||||
|
||||
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
|
||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||
}];
|
||||
@@ -819,7 +853,11 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
// FIXME: Shouldn't deletes be sorted in descending order?
|
||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||
|
||||
[self prepareForDeleteRowsAtIndexPaths:sortedIndexPaths];
|
||||
|
||||
[_editingTransactionQueue addOperationWithBlock:^{
|
||||
[self willDeleteRowsAtIndexPaths:sortedIndexPaths];
|
||||
|
||||
LOG(@"Edit Transaction - deleteRows: %@", indexPaths);
|
||||
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
|
||||
}];
|
||||
@@ -853,8 +891,12 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection]];
|
||||
}
|
||||
|
||||
[self prepareForReloadRowsAtIndexPaths:indexPaths];
|
||||
|
||||
[_editingTransactionQueue addOperationWithBlock:^{
|
||||
[self willReloadRowsAtIndexPaths:indexPaths];
|
||||
|
||||
LOG(@"Edit Transaction - reloadRows: %@", indexPaths);
|
||||
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
|
||||
[self _batchLayoutNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||
|
||||
@@ -47,7 +47,12 @@ extern NSArray *ASFindElementsInMultidimensionalArrayAtIndexPaths(NSMutableArray
|
||||
/**
|
||||
* Return all the index paths of mutable multidimensional array at given index set, in ascending order.
|
||||
*/
|
||||
extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *MultidimensionalArray, NSIndexSet *indexSet);
|
||||
extern NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensionalArray, NSIndexSet *indexSet);
|
||||
|
||||
/**
|
||||
* Return the index paths of the given multidimensional array that are present in the given index paths array.
|
||||
*/
|
||||
extern NSArray<NSIndexPath *> *ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths(NSArray *multidimensionalArray, NSArray<NSIndexPath *> *indexPaths);
|
||||
|
||||
/**
|
||||
* Return all the index paths of a two-dimensional array, in ascending order.
|
||||
|
||||
@@ -53,6 +53,28 @@ static void ASRecursivelyFindIndexPathsForMultidimensionalArray(NSObject *obj, N
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL ASElementExistsAtIndexPathForMultidimensionalArray(NSArray *array, NSIndexPath *indexPath) {
|
||||
NSUInteger indexLength = indexPath.length;
|
||||
ASDisplayNodeCAssert(indexLength != 0, @"Must have a non-zero indexPath length");
|
||||
NSUInteger firstIndex = [indexPath indexAtPosition:0];
|
||||
BOOL elementExists = firstIndex < array.count;
|
||||
|
||||
if (indexLength == 1) {
|
||||
return elementExists;
|
||||
}
|
||||
|
||||
if (!elementExists) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSUInteger indexesLength = indexLength - 1;
|
||||
NSUInteger indexes[indexesLength];
|
||||
[indexPath getIndexes:indexes range:NSMakeRange(1, indexesLength)];
|
||||
NSIndexPath *indexPathByRemovingFirstIndex = [NSIndexPath indexPathWithIndexes:indexes length:indexesLength];
|
||||
|
||||
return ASElementExistsAtIndexPathForMultidimensionalArray(array[firstIndex], indexPathByRemovingFirstIndex);
|
||||
}
|
||||
|
||||
#pragma mark - Public Methods
|
||||
|
||||
NSObject<NSCopying> *ASMultidimensionalArrayDeepMutableCopy(NSObject<NSCopying> *obj)
|
||||
@@ -142,6 +164,18 @@ NSArray *ASIndexPathsForMultidimensionalArrayAtIndexSet(NSArray *multidimensiona
|
||||
return res;
|
||||
}
|
||||
|
||||
NSArray<NSIndexPath *> *ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths(NSArray *multidimensionalArray, NSArray<NSIndexPath *> *indexPaths)
|
||||
{
|
||||
NSMutableArray *res = [NSMutableArray array];
|
||||
for (NSIndexPath *indexPath in indexPaths) {
|
||||
if (ASElementExistsAtIndexPathForMultidimensionalArray(multidimensionalArray, indexPath)) {
|
||||
[res addObject:indexPath];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
NSArray *ASIndexPathsForTwoDimensionalArray(NSArray <NSArray *>* twoDimensionalArray)
|
||||
{
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
|
||||
Reference in New Issue
Block a user