[ASDataController] Unify ASDataController and ASCollectionDataController

This commit is contained in:
Adlai Holler
2016-08-18 21:24:08 -07:00
committed by Adlai Holler
parent 0bd664f9ed
commit bb6203347f
6 changed files with 249 additions and 195 deletions

View File

@@ -77,7 +77,6 @@
205F0E1E1B373A2C007741D0 /* ASCollectionViewLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */; };
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; };
242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; };
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; };
251B8EFA1BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; };
2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; };
254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */; };
@@ -249,7 +248,6 @@
92DD2FE81BF4D0A80074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
92DD2FE91BF4D4870074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
92DD2FEA1BF4D49B0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; };
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; };
9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */; };
9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */; };
@@ -263,7 +261,6 @@
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */; };
9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */; };
9C70F20B1CDBE9A4007D6C76 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; };
9C70F20C1CDBE9B6007D6C76 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; };
9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */; };
9C70F20E1CDBE9E5007D6C76 /* NSArray+Diffing.h in Headers */ = {isa = PBXBuildFile; fileRef = DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C70F20F1CDBE9FF007D6C76 /* ASLayoutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */; };
@@ -575,7 +572,6 @@
F7CE6C851D2CDB5800BE4C15 /* NSIndexSet+ASHelpers.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */; };
F7CE6C861D2CDB5800BE4C15 /* _ASDisplayViewAccessiblity.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 69CB62A91CB8165900024920 /* _ASDisplayViewAccessiblity.h */; };
F7CE6C871D2CDB5800BE4C15 /* ASDataController+Subclasses.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; };
F7CE6C8B1D2CDB5800BE4C15 /* ASCollectionDataController.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; };
F7CE6C8C1D2CDB5800BE4C15 /* _ASCoreAnimationExtras.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */; };
F7CE6C8D1D2CDB5800BE4C15 /* _ASHierarchyChangeSet.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; };
F7CE6C8F1D2CDB5800BE4C15 /* _ASScopeTimer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 058D0A07195D050800B7D73C /* _ASScopeTimer.h */; };
@@ -747,7 +743,6 @@
F7CE6C861D2CDB5800BE4C15 /* _ASDisplayViewAccessiblity.h in CopyFiles */,
F7CE6C871D2CDB5800BE4C15 /* ASDataController+Subclasses.h in CopyFiles */,
F7CE6C451D2CDB3E00BE4C15 /* ASPINRemoteImageDownloader.h in CopyFiles */,
F7CE6C8B1D2CDB5800BE4C15 /* ASCollectionDataController.h in CopyFiles */,
F7CE6C591D2CDB3E00BE4C15 /* UIView+ASConvenience.h in CopyFiles */,
F7CE6C5A1D2CDB3E00BE4C15 /* ASTraitCollection.h in CopyFiles */,
F7CE6C8C1D2CDB5800BE4C15 /* _ASCoreAnimationExtras.h in CopyFiles */,
@@ -891,8 +886,6 @@
205F0E1F1B376416007741D0 /* CGRect+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CGRect+ASConvenience.h"; sourceTree = "<group>"; };
205F0E201B376416007741D0 /* CGRect+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CGRect+ASConvenience.m"; sourceTree = "<group>"; };
242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = "<group>"; };
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASCollectionDataController.h; sourceTree = "<group>"; };
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionDataController.mm; sourceTree = "<group>"; };
251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = "<group>"; };
251B8EF51BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = "<group>"; };
251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = "<group>"; };
@@ -1582,8 +1575,6 @@
children = (
DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */,
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */,
251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */,
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
464052191A3F83C40061C0BA /* ASDataController.h */,
4640521A1A3F83C40061C0BA /* ASDataController.mm */,
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */,
@@ -1725,7 +1716,6 @@
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
9C70F20C1CDBE9B6007D6C76 /* ASCollectionDataController.h in Headers */,
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */,
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
@@ -2139,7 +2129,6 @@
E5711A2E1C840C96009619D4 /* ASIndexedNodeContext.mm in Sources */,
058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */,
9C8221971BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
251B8EF81BBB3D690087C538 /* ASCollectionDataController.mm in Sources */,
ACF6ED301B17843500DA7C62 /* ASStackLayoutSpec.mm in Sources */,
257754BE1BEE458E00737CA5 /* ASTextKitComponents.m in Sources */,
257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */,
@@ -2228,7 +2217,6 @@
92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */,
636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */,
9B92C8861BC2EB7600EE46B2 /* ASCollectionViewFlowLayoutInspector.m in Sources */,
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */,
B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.mm in Sources */,
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */,
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm in Sources */,

View File

@@ -13,7 +13,7 @@
#import "ASBatchFetching.h"
#import "ASDelegateProxy.h"
#import "ASCellNode+Internal.h"
#import "ASCollectionDataController.h"
#import "ASChangeSetDataController.h"
#import "ASCollectionViewLayoutController.h"
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASDisplayNodeExtras.h"
@@ -86,11 +86,11 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
#pragma mark -
#pragma mark ASCollectionView.
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASCollectionDataControllerSource, ASCellNodeInteractionDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView, ASDataControllerEnvironmentDelegate> {
@interface ASCollectionView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, ASCellNodeInteractionDelegate, ASDelegateProxyInterceptor, ASBatchFetchingScrollView, ASDataControllerEnvironmentDelegate> {
ASCollectionViewProxy *_proxyDataSource;
ASCollectionViewProxy *_proxyDelegate;
ASCollectionDataController *_dataController;
ASDataController *_dataController;
ASRangeController *_rangeController;
ASCollectionViewLayoutController *_layoutController;
id<ASCollectionViewLayoutInspecting> _defaultLayoutInspector;
@@ -229,7 +229,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
_rangeController.delegate = self;
_rangeController.layoutController = _layoutController;
_dataController = [[ASCollectionDataController alloc] initWithDataSource:self];
_dataController = [[ASChangeSetDataController alloc] initWithDataSource:self];
_dataController.delegate = _rangeController;
_dataController.environmentDelegate = self;
@@ -1011,24 +1011,24 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
#pragma mark - ASCollectionViewDataControllerSource Supplementary view support
- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
- (ASCellNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ASCellNode *node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath];
ASDisplayNodeAssert(node != nil, @"A node must be returned for a supplementary node");
return node;
}
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController
- (NSArray *)supplementaryNodeKindsInDataController:(ASDataController *)dataController
{
return [_registeredSupplementaryKinds allObjects];
}
- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
return [self.layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
}
- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
return [self.layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section];
}

View File

@@ -57,6 +57,21 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
*/
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController;
@optional
/**
The constrained size range for layout.
*/
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (NSArray *)supplementaryNodeKindsInDataController:(ASDataController *)dataController;
- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section;
- (ASCellNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (ASCellNodeBlock)dataController:(ASDataController *)dataController supplementaryNodeBlockOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
@end
@protocol ASDataControllerEnvironmentDelegate
@@ -183,6 +198,8 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
- (nullable ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
/**
* Direct access to the nodes that have completed calculation and layout
*/

View File

@@ -59,6 +59,10 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
BOOL _delegateDidDeleteNodes;
BOOL _delegateDidInsertSections;
BOOL _delegateDidDeleteSections;
BOOL _dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath;
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
}
@end
@@ -82,6 +86,12 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
_completedNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
_editingNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
_pendingContexts = [NSMutableDictionary dictionary];
_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath = [dataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:)];
ASDisplayNodeAssertTrue(_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath || [dataSource respondsToSelector:@selector(dataController:supplementaryNodeOfKind:atIndexPath:)]);
_mainSerialQueue = [[ASMainSerialQueue alloc] init];
const char *queueName = [[NSString stringWithFormat:@"org.AsyncDisplayKit.ASDataController.editingTransactionQueue:%p", self] cStringUsingEncoding:NSASCIIStringEncoding];
@@ -217,9 +227,14 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath];
if ([kind isEqualToString:ASDataControllerRowNodeKind]) {
return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath];
} else {
return [_dataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
}
}
#pragma mark - External Data Querying + Editing
- (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(ASDataControllerCompletionBlock)completionBlock
@@ -399,7 +414,12 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
// Allow subclasses to perform setup before going into the edit transaction
[self prepareForReloadData];
for (NSString *kind in [self supplementaryKinds]) {
LOG(@"Populating elements of kind: %@", kind);
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
[self _populateSupplementaryNodesOfKind:kind withMutableContexts:contexts];
_pendingContexts[kind] = contexts;
}
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
LOG(@"Edit Transaction - reloadData");
@@ -414,7 +434,28 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
[self _deleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions];
}
[self willReloadData];
// Supplementary node handling
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, __unused BOOL * _Nonnull stop) {
// Remove everything that existed before the reload, now that we're ready to insert replacements
NSArray *indexPaths = [self indexPathsForEditingNodesOfKind:kind];
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
NSArray *editingNodes = [self editingNodesOfKind:kind];
NSIndexSet *indexSet = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, editingNodes.count)];
[self deleteSectionsOfKind:kind atIndexSet:indexSet completion:nil];
// Insert each section
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (int i = 0; i < sectionCount; i++) {
[sections addObject:[NSMutableArray array]];
}
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil];
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
}];
}];
[_pendingContexts removeAllObjects];
// Insert empty sections
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
@@ -477,6 +518,77 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
return contexts;
}
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
{
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
id<ASDataControllerSource> source = self.dataSource;
NSUInteger sectionCount = self.itemCountsFromDataSource.size();
for (NSUInteger i = 0; i < sectionCount; i++) {
NSUInteger rowCount = [source dataController:self supplementaryNodesOfKind:kind inSection:i];
for (NSUInteger j = 0; j < rowCount; j++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i];
[self _populateSupplementaryNodeOfKind:kind atIndexPath:indexPath mutableContexts:contexts environmentTraitCollection:environmentTraitCollection];
}
}
}
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
{
id<ASEnvironment> environment = [self.environmentDelegate dataControllerEnvironment];
ASEnvironmentTraitCollection environmentTraitCollection = environment.environmentTraitCollection;
[sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
for (NSUInteger sec = range.location; sec < NSMaxRange(range); sec++) {
NSUInteger itemCount = [self.dataSource dataController:self supplementaryNodesOfKind:kind inSection:sec];
for (NSUInteger i = 0; i < itemCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec];
[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 enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
for (NSUInteger sec = range.location; sec < NSMaxRange(range); sec++) {
NSUInteger itemCount = [self.dataSource dataController:self supplementaryNodesOfKind:kind inSection:sec];
for (NSUInteger i = 0; i < itemCount; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:sec];
[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.dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath];
} else {
ASCellNode *supplementaryNode = [self.dataSource 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];
}
- (void)invalidateDataSourceItemCounts
{
ASDisplayNodeAssertMainThread();
@@ -500,6 +612,13 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
return _itemCountsFromDataSource;
}
#pragma mark - Private Helpers
- (NSArray *)supplementaryKinds
{
return [self.dataSource supplementaryNodeKindsInDataController:self];
}
#pragma mark - Batching (External API)
- (void)beginUpdates
@@ -557,10 +676,28 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sections];
[self prepareForInsertSections:sections];
// Supplementary node handling
for (NSString *kind in [self supplementaryKinds]) {
LOG(@"Populating elements of kind: %@, for sections: %@", kind, sections);
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
[self _populateSupplementaryNodesOfKind:kind withSections:sections mutableContexts:contexts];
_pendingContexts[kind] = contexts;
}
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willInsertSections:sections];
// Supplementary node handling
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count];
for (NSUInteger i = 0; i < sections.count; i++) {
[sectionArray addObject:[NSMutableArray array]];
}
[self insertSections:sectionArray ofKind:kind atIndexSet:sections completion:nil];
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
}];
}];
[_pendingContexts removeAllObjects];
LOG(@"Edit Transaction - insertSections: %@", sections);
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count];
@@ -584,7 +721,13 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willDeleteSections:sections];
// Supplementary node handling
for (NSString *kind in [self supplementaryKinds]) {
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections);
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
[self deleteSectionsOfKind:kind atIndexSet:sections completion:nil];
}
// remove elements
LOG(@"Edit Transaction - deleteSections: %@", sections);
@@ -611,7 +754,23 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willMoveSection:section toSection:newSection];
// Supplementary node handling
NSIndexSet *sectionAsIndexSet = [NSIndexSet indexSetWithIndex:section];
for (NSString *kind in [self supplementaryKinds]) {
NSMutableArray *editingNodes = [self editingNodesOfKind:kind];
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet(editingNodes, sectionAsIndexSet);
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
[self deleteNodesOfKind:kind atIndexPaths:indexPaths completion:nil];
// update the section of indexpaths
NSMutableArray *updatedIndexPaths = [[NSMutableArray alloc] initWithCapacity:indexPaths.count];
for (NSIndexPath *indexPath in indexPaths) {
NSUInteger newItem = [indexPath indexAtPosition:indexPath.length - 1];
NSIndexPath *mappedIndexPath = [NSIndexPath indexPathForItem:newItem inSection:newSection];
[updatedIndexPaths addObject:mappedIndexPath];
}
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
}
// remove elements
@@ -633,59 +792,6 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
});
}
#pragma mark - Backing store manipulation optional hooks (Subclass API)
- (void)prepareForReloadData
{
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
}
- (void)willReloadData
{
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
}
- (void)prepareForInsertSections:(NSIndexSet *)sections
{
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
}
- (void)willInsertSections:(NSIndexSet *)sections
{
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
}
- (void)willDeleteSections:(NSIndexSet *)sections
{
// Optional template hook for subclasses (See ASDataController+Subclasses.h)
}
- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection
{
// 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)
}
#pragma mark - Row Editing (External API)
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
@@ -714,10 +820,23 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
environmentTraitCollection:environmentTraitCollection]];
}
[self prepareForInsertRowsAtIndexPaths:indexPaths];
// Supplementary node handling
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;
}
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willInsertRowsAtIndexPaths:indexPaths];
// Supplementary node handling
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
}];
}];
[_pendingContexts removeAllObjects];
LOG(@"Edit Transaction - insertRows: %@", indexPaths);
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
@@ -740,10 +859,34 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
// FIXME: Shouldn't deletes be sorted in descending order?
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
[self prepareForDeleteRowsAtIndexPaths:sortedIndexPaths];
// Supplementary node handling
for (NSString *kind in [self supplementaryKinds]) {
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
[self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts];
_pendingContexts[kind] = contexts;
}
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
[self willDeleteRowsAtIndexPaths:sortedIndexPaths];
// Supplementary node handling
for (NSString *kind in [self supplementaryKinds]) {
NSArray<NSIndexPath *> *deletedIndexPaths = ASIndexPathsInMultidimensionalArrayIntersectingIndexPaths([self editingNodesOfKind:kind], indexPaths);
[self deleteNodesOfKind:kind atIndexPaths:deletedIndexPaths completion:nil];
// If any of the contexts remain after the deletion, re-insert them, e.g.
// UICollectionElementKindSectionHeader remains even if item 0 is deleted.
NSMutableArray<ASIndexedNodeContext *> *reinsertedContexts = [NSMutableArray array];
for (ASIndexedNodeContext *context in _pendingContexts[kind]) {
if ([deletedIndexPaths containsObject:context.indexPath]) {
[reinsertedContexts addObject:context];
}
}
[self batchLayoutNodesFromContexts:reinsertedContexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
}];
}
[_pendingContexts removeAllObjects];
LOG(@"Edit Transaction - deleteRows: %@", indexPaths);
[self _deleteNodesAtIndexPaths:sortedIndexPaths withAnimationOptions:animationOptions];
@@ -890,6 +1033,21 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
return nil;
}
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssertMainThread();
NSArray *nodesOfKind = [self completedNodesOfKind:kind];
NSInteger section = indexPath.section;
if (section < nodesOfKind.count) {
NSArray *nodesOfKindInSection = nodesOfKind[section];
NSInteger itemIndex = indexPath.item;
if (itemIndex < nodesOfKindInSection.count) {
return nodesOfKindInSection[itemIndex];
}
}
return nil;
}
/// Returns nodes that can be queried externally. _externalCompletedNodes is used if available, _completedNodes otherwise.
- (NSArray *)completedNodes
{

View File

@@ -86,113 +86,4 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCellNode *> *nodes, NS
*/
- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock;
#pragma mark - Data Manipulation Hooks
/**
* Notifies the subclass to perform any work needed before the data controller is reloaded entirely
*
* @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.
*/
- (void)prepareForReloadData;
/**
* Notifies the subclass that the data controller is about to reload its data entirely
*
* @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 new node creation like supplementary views
* or header/footer nodes.
*/
- (void)willReloadData;
/**
* Notifies the subclass to perform setup before sections 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 sections Indices of sections to be inserted
*/
- (void)prepareForInsertSections:(NSIndexSet *)sections;
/**
* Notifies the subclass that the data controller will insert new sections at the given position
*
* @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 sections Indices of sections to be inserted
*/
- (void)willInsertSections:(NSIndexSet *)sections;
/**
* Notifies the subclass that the data controller will delete sections at the given positions
*
* @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 sections Indices of sections to be deleted
*/
- (void)willDeleteSections:(NSIndexSet *)sections;
/**
* Notifies the subclass that the data controller will move a section to a new position
*
* @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 section Index of current section position
* @param newSection Index of new section position
*/
- (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;
@end

View File

@@ -10,7 +10,7 @@
#import <XCTest/XCTest.h>
#import "ASCollectionView.h"
#import "ASCollectionDataController.h"
#import "ASDataController.h"
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASCellNode.h"
#import "ASCollectionNode.h"
@@ -108,7 +108,7 @@
@interface ASCollectionView (InternalTesting)
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController;
- (NSArray *)supplementaryNodeKindsInDataController:(ASDataController *)dataController;
@end