mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-04-23 11:27:56 +08:00
Introduce ASSectionContext and ASSection (#2178)
- Objects conform to ASSectionContext protocol can be provided via ASCollectionDataSource and later retrieved from the collection view. They are guaranteed to be in sync with sections of the collection view. They can be used to store additional data associated with each section, to be used in collection view layout and the like. - ASSection is an internal object that is the foundation for coming debugging tools. - Unit tests included.
This commit is contained in:
@@ -298,8 +298,13 @@
|
||||
AC3C4A521A1139C100143C57 /* ASCollectionView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A501A1139C100143C57 /* ASCollectionView.mm */; };
|
||||
AC47D9421B3B891B00AAEE9D /* ASCellNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.mm */; };
|
||||
AC47D9461B3BB41900AAEE9D /* ASRelativeSize.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */; };
|
||||
AC6145411D8AFAE8003D62A2 /* ASSection.h in Headers */ = {isa = PBXBuildFile; fileRef = AC6145401D8AFAE8003D62A2 /* ASSection.h */; };
|
||||
AC6145431D8AFD4F003D62A2 /* ASSection.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6145421D8AFD4F003D62A2 /* ASSection.m */; };
|
||||
AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */ = {isa = PBXBuildFile; fileRef = AC6145421D8AFD4F003D62A2 /* ASSection.m */; };
|
||||
AC6456091B0A335000CF11B8 /* ASCellNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC6456071B0A335000CF11B8 /* ASCellNode.mm */; };
|
||||
AC7A2C181BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */; };
|
||||
ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */ = {isa = PBXBuildFile; fileRef = ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
ACE87A331D73726300D7FF06 /* ASSectionContext.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */; };
|
||||
ACF6ED1B1B17843500DA7C62 /* ASBackgroundLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */; };
|
||||
ACF6ED1D1B17843500DA7C62 /* ASCenterLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */; };
|
||||
ACF6ED211B17843500DA7C62 /* ASDimension.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED081B17843500DA7C62 /* ASDimension.mm */; };
|
||||
@@ -647,6 +652,7 @@
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
CC88F7AE1D80AF5E000D6D4E /* ASObjectDescriptionHelpers.h in CopyFiles */,
|
||||
ACE87A331D73726300D7FF06 /* ASSectionContext.h in CopyFiles */,
|
||||
F7CE6C981D2CDB5800BE4C15 /* ASInternalHelpers.h in CopyFiles */,
|
||||
F7CE6CB71D2CE2D000BE4C15 /* ASLayoutableExtensibility.h in CopyFiles */,
|
||||
F7CE6C131D2CDB3E00BE4C15 /* ASPagerFlowLayout.h in CopyFiles */,
|
||||
@@ -1045,9 +1051,12 @@
|
||||
AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewProtocols.h; sourceTree = "<group>"; };
|
||||
AC47D9431B3BB41900AAEE9D /* ASRelativeSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeSize.h; path = AsyncDisplayKit/Layout/ASRelativeSize.h; sourceTree = "<group>"; };
|
||||
AC47D9441B3BB41900AAEE9D /* ASRelativeSize.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeSize.mm; path = AsyncDisplayKit/Layout/ASRelativeSize.mm; sourceTree = "<group>"; };
|
||||
AC6145401D8AFAE8003D62A2 /* ASSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASSection.h; path = ../Private/ASSection.h; sourceTree = "<group>"; };
|
||||
AC6145421D8AFD4F003D62A2 /* ASSection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASSection.m; path = ../Private/ASSection.m; sourceTree = "<group>"; };
|
||||
AC6456071B0A335000CF11B8 /* ASCellNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCellNode.mm; sourceTree = "<group>"; };
|
||||
AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableViewInternal.h; sourceTree = "<group>"; };
|
||||
ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASViewController.h; sourceTree = "<group>"; };
|
||||
ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASSectionContext.h; path = Details/ASSectionContext.h; sourceTree = "<group>"; };
|
||||
ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = "<group>"; };
|
||||
ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASBackgroundLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm; sourceTree = "<group>"; };
|
||||
ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCenterLayoutSpec.h; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.h; sourceTree = "<group>"; };
|
||||
@@ -1311,6 +1320,7 @@
|
||||
25E327551C16819500A2170C /* ASPagerNode.m */,
|
||||
A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */,
|
||||
A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */,
|
||||
ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */,
|
||||
D785F6601A74327E00291744 /* ASScrollNode.h */,
|
||||
D785F6611A74327E00291744 /* ASScrollNode.m */,
|
||||
68FC85E01CE29B7E00EDD713 /* ASTabBarController.h */,
|
||||
@@ -1633,6 +1643,8 @@
|
||||
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.mm */,
|
||||
E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */,
|
||||
E5711A2D1C840C96009619D4 /* ASIndexedNodeContext.mm */,
|
||||
AC6145401D8AFAE8003D62A2 /* ASSection.h */,
|
||||
AC6145421D8AFD4F003D62A2 /* ASSection.m */,
|
||||
);
|
||||
name = "Data Controller";
|
||||
sourceTree = "<group>";
|
||||
@@ -1774,6 +1786,7 @@
|
||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */,
|
||||
B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */,
|
||||
ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */,
|
||||
254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */,
|
||||
509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */,
|
||||
CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */,
|
||||
@@ -1829,6 +1842,7 @@
|
||||
DE8BEAC21C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */,
|
||||
B35062041B010EFD0018CF92 /* ASMultiplexImageNode.h in Headers */,
|
||||
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
AC6145411D8AFAE8003D62A2 /* ASSection.h in Headers */,
|
||||
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */,
|
||||
B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */,
|
||||
B13CA0F81C519EBA00E031AB /* ASCollectionViewLayoutFacilitatorProtocol.h in Headers */,
|
||||
@@ -2165,6 +2179,7 @@
|
||||
257754B01BEE44CD00737CA5 /* ASTextKitRenderer+TextChecking.mm in Sources */,
|
||||
0516FA411A1563D200B4EBED /* ASMultiplexImageNode.mm in Sources */,
|
||||
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */,
|
||||
AC6145431D8AFD4F003D62A2 /* ASSection.m in Sources */,
|
||||
058D0A1B195D050800B7D73C /* ASMutableAttributedStringBuilder.m in Sources */,
|
||||
055B9FA91A1C154B00035D6D /* ASNetworkImageNode.mm in Sources */,
|
||||
AEB7B01B1C5962EA00662EF4 /* ASDefaultPlayButton.m in Sources */,
|
||||
@@ -2346,6 +2361,7 @@
|
||||
254C6B821BF94F8A003EC431 /* ASTextKitComponents.m in Sources */,
|
||||
430E7C921B4C23F100697A4C /* ASIndexPath.m in Sources */,
|
||||
34EFC7601B701C8B00AD841F /* ASInsetLayoutSpec.mm in Sources */,
|
||||
AC6145441D8AFD4F003D62A2 /* ASSection.m in Sources */,
|
||||
34EFC75E1B701BF000AD841F /* ASInternalHelpers.m in Sources */,
|
||||
34EFC7681B701CDE00AD841F /* ASLayout.mm in Sources */,
|
||||
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
@protocol ASCollectionDataSource;
|
||||
@protocol ASCollectionDelegate;
|
||||
@protocol ASCollectionViewLayoutInspecting;
|
||||
@protocol ASSectionContext;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -230,6 +231,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section;
|
||||
|
||||
/**
|
||||
* Inserts items at the locations identified by an array of index paths.
|
||||
*
|
||||
@@ -420,6 +423,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (nullable id<ASSectionContext>)collectionView:(ASCollectionView *)collectionView contextForSection:(NSInteger)section;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#import "ASCollectionNode.h"
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "ASCollectionViewLayoutFacilitatorProtocol.h"
|
||||
#import "ASSectionContext.h"
|
||||
|
||||
|
||||
/// What, if any, invalidation should we perform during the next -layoutSubviews.
|
||||
@@ -153,6 +154,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
unsigned int asyncDataSourceNodeForItemAtIndexPath:1;
|
||||
unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1;
|
||||
unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1;
|
||||
unsigned int asyncDataSourceContextForSection:1;
|
||||
} _asyncDataSourceFlags;
|
||||
|
||||
struct {
|
||||
@@ -348,6 +350,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
_asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceContextForSection = [_asyncDataSource respondsToSelector:@selector(collectionView:contextForSection:)];
|
||||
|
||||
// Data-source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath:
|
||||
ASDisplayNodeAssertTrue(_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath || _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath);
|
||||
@@ -586,6 +589,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
[_dataController moveSection:section toSection:newSection withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)contextForSection:(NSInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return [_dataController contextForSection:section];
|
||||
}
|
||||
|
||||
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -985,7 +994,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
return self.strongCollectionNode;
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionViewDataControllerSource Supplementary view support
|
||||
#pragma mark - ASCollectionViewDataControllerSource
|
||||
|
||||
- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
@@ -1009,6 +1018,21 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
return [self.layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
id<ASSectionContext> context = nil;
|
||||
|
||||
if (_asyncDataSourceFlags.asyncDataSourceContextForSection) {
|
||||
context = [_asyncDataSource collectionView:self contextForSection:section];
|
||||
}
|
||||
|
||||
if (context != nil) {
|
||||
context.collectionView = self;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
#pragma mark - ASRangeControllerDataSource
|
||||
|
||||
- (ASRangeController *)rangeController
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#import <AsyncDisplayKit/ASCollectionViewFlowLayoutInspector.h>
|
||||
#import <AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h>
|
||||
#import <AsyncDisplayKit/ASCellNode.h>
|
||||
#import <AsyncDisplayKit/ASSectionContext.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASScrollNode.h>
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
@class ASDisplayNode;
|
||||
@class ASCollectionDataController;
|
||||
@protocol ASDataControllerSource;
|
||||
@protocol ASSectionContext;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol ASCollectionDataControllerSource <ASDataControllerSource>
|
||||
|
||||
@@ -28,6 +31,8 @@
|
||||
|
||||
- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section;
|
||||
|
||||
- (nullable id<ASSectionContext>)dataController:(ASCollectionDataController *)dataController contextForSection:(NSInteger)section;
|
||||
|
||||
@optional
|
||||
|
||||
- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
@@ -42,4 +47,8 @@
|
||||
|
||||
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -15,12 +15,17 @@
|
||||
#import "ASCellNode.h"
|
||||
#import "ASDataController+Subclasses.h"
|
||||
#import "ASIndexedNodeContext.h"
|
||||
#import "ASSection.h"
|
||||
#import "ASSectionContext.h"
|
||||
|
||||
//#define LOG(...) NSLog(__VA_ARGS__)
|
||||
#define LOG(...)
|
||||
|
||||
@interface ASCollectionDataController () {
|
||||
BOOL _dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath;
|
||||
NSInteger _nextSectionID;
|
||||
NSMutableArray<ASSection *> *_sections;
|
||||
NSArray<ASSection *> *_pendingSections;
|
||||
}
|
||||
|
||||
- (id<ASCollectionDataControllerSource>)collectionDataSource;
|
||||
@@ -28,17 +33,20 @@
|
||||
@end
|
||||
|
||||
@implementation ASCollectionDataController {
|
||||
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingContexts;
|
||||
NSMutableDictionary<NSString *, NSMutableArray<ASIndexedNodeContext *> *> *_pendingNodeContexts;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDataSource:(id<ASCollectionDataControllerSource>)dataSource
|
||||
{
|
||||
self = [super initWithDataSource:dataSource];
|
||||
if (self != nil) {
|
||||
_pendingContexts = [NSMutableDictionary dictionary];
|
||||
_pendingNodeContexts = [NSMutableDictionary dictionary];
|
||||
_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath = [dataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:)];
|
||||
|
||||
ASDisplayNodeAssertTrue(_dataSourceImplementsSupplementaryNodeBlockOfKindAtIndexPath || [dataSource respondsToSelector:@selector(dataController:supplementaryNodeOfKind:atIndexPath:)]);
|
||||
|
||||
_nextSectionID = 0;
|
||||
_sections = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -46,17 +54,25 @@
|
||||
- (void)prepareForReloadDataWithSectionCount:(NSInteger)newSectionCount
|
||||
{
|
||||
NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newSectionCount)];
|
||||
|
||||
[_sections removeAllObjects];
|
||||
[self _populatePendingSectionsFromDataSource:sections];
|
||||
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
LOG(@"Populating elements of kind: %@", kind);
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[self _populateSupplementaryNodesOfKind:kind withSections:sections mutableContexts:contexts];
|
||||
_pendingContexts[kind] = contexts;
|
||||
_pendingNodeContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willReloadDataWithSectionCount:(NSInteger)newSectionCount
|
||||
{
|
||||
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, __unused BOOL * _Nonnull stop) {
|
||||
NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newSectionCount)];
|
||||
|
||||
[self applyPendingSections:sectionIndexes];
|
||||
|
||||
[_pendingNodeContexts 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];
|
||||
@@ -70,28 +86,32 @@
|
||||
for (int i = 0; i < newSectionCount; i++) {
|
||||
[sections addObject:[NSMutableArray array]];
|
||||
}
|
||||
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newSectionCount)] completion:nil];
|
||||
[self insertSections:sections ofKind:kind atIndexSet:sectionIndexes completion:nil];
|
||||
|
||||
[self batchLayoutNodesFromContexts:contexts batchCompletion:^(NSArray<ASCellNode *> *nodes, NSArray<NSIndexPath *> *indexPaths) {
|
||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
}];
|
||||
}];
|
||||
[_pendingContexts removeAllObjects];
|
||||
[_pendingNodeContexts removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)prepareForInsertSections:(NSIndexSet *)sections
|
||||
{
|
||||
[self _populatePendingSectionsFromDataSource:sections];
|
||||
|
||||
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;
|
||||
_pendingNodeContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willInsertSections:(NSIndexSet *)sections
|
||||
{
|
||||
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
|
||||
[self applyPendingSections:sections];
|
||||
|
||||
[_pendingNodeContexts 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]];
|
||||
@@ -102,11 +122,13 @@
|
||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
}];
|
||||
}];
|
||||
[_pendingContexts removeAllObjects];
|
||||
[_pendingNodeContexts removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)willDeleteSections:(NSIndexSet *)sections
|
||||
{
|
||||
[_sections removeObjectsAtIndexes:sections];
|
||||
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSArray *indexPaths = ASIndexPathsForMultidimensionalArrayAtIndexSet([self editingNodesOfKind:kind], sections);
|
||||
|
||||
@@ -117,6 +139,10 @@
|
||||
|
||||
- (void)willMoveSection:(NSInteger)section toSection:(NSInteger)newSection
|
||||
{
|
||||
ASSection *movedSection = [_sections objectAtIndex:section];
|
||||
[_sections removeObjectAtIndex:section];
|
||||
[_sections insertObject:movedSection atIndex:newSection];
|
||||
|
||||
NSIndexSet *sectionAsIndexSet = [NSIndexSet indexSetWithIndex:section];
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSMutableArray *editingNodes = [self editingNodesOfKind:kind];
|
||||
@@ -141,19 +167,19 @@
|
||||
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;
|
||||
_pendingNodeContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
[_pendingContexts enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSMutableArray<ASIndexedNodeContext *> * _Nonnull contexts, BOOL * _Nonnull stop) {
|
||||
[_pendingNodeContexts 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];
|
||||
[_pendingNodeContexts removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)prepareForDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
@@ -161,7 +187,7 @@
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts];
|
||||
_pendingContexts[kind] = contexts;
|
||||
_pendingNodeContexts[kind] = contexts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +201,7 @@
|
||||
// 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]) {
|
||||
for (ASIndexedNodeContext *context in _pendingNodeContexts[kind]) {
|
||||
if ([deletedIndexPaths containsObject:context.indexPath]) {
|
||||
[reinsertedContexts addObject:context];
|
||||
}
|
||||
@@ -185,7 +211,20 @@
|
||||
[self insertNodes:nodes ofKind:kind atIndexPaths:indexPaths completion:nil];
|
||||
}];
|
||||
}
|
||||
[_pendingContexts removeAllObjects];
|
||||
[_pendingNodeContexts removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)_populatePendingSectionsFromDataSource:(NSIndexSet *)sectionIndexes
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
NSMutableArray<ASSection *> *sections = [NSMutableArray arrayWithCapacity:sectionIndexes.count];
|
||||
[sectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
id<ASSectionContext> context = [self.collectionDataSource dataController:self contextForSection:idx];
|
||||
[sections addObject:[[ASSection alloc] initWithSectionID:_nextSectionID context:context]];
|
||||
_nextSectionID++;
|
||||
}];
|
||||
_pendingSections = sections;
|
||||
}
|
||||
|
||||
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withSections:(NSIndexSet *)sections mutableContexts:(NSMutableArray<ASIndexedNodeContext *> *)contexts
|
||||
@@ -254,7 +293,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - External supplementary store querying
|
||||
#pragma mark - External supplementary store and section context querying
|
||||
|
||||
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
@@ -271,6 +310,13 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)contextForSection:(NSInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssertTrue(section >= 0 && section < _sections.count);
|
||||
return _sections[section].context;
|
||||
}
|
||||
|
||||
#pragma mark - Private Helpers
|
||||
|
||||
- (NSArray *)supplementaryKinds
|
||||
@@ -283,4 +329,10 @@
|
||||
return (id<ASCollectionDataControllerSource>)self.dataSource;
|
||||
}
|
||||
|
||||
- (void)applyPendingSections:(NSIndexSet *)sectionIndexes
|
||||
{
|
||||
[_sections insertObjects:_pendingSections atIndexes:sectionIndexes];
|
||||
_pendingSections = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -396,8 +396,8 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
[self invalidateDataSourceItemCounts];
|
||||
NSUInteger sectionCount = [self itemCountsFromDataSource].size();
|
||||
NSIndexSet *sectionIndexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sectionIndexSet];
|
||||
NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateNodeContextsFromDataSourceForSections:sectionIndexes];
|
||||
|
||||
// Allow subclasses to perform setup before going into the edit transaction
|
||||
[self prepareForReloadDataWithSectionCount:sectionCount];
|
||||
@@ -422,7 +422,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
[sections addObject:[[NSMutableArray alloc] init]];
|
||||
}
|
||||
[self _insertSections:sections atIndexSet:sectionIndexSet withAnimationOptions:animationOptions];
|
||||
[self _insertSections:sections atIndexSet:sectionIndexes withAnimationOptions:animationOptions];
|
||||
|
||||
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||
|
||||
@@ -451,7 +451,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
/**
|
||||
* Fetches row contexts for the provided sections from the data source.
|
||||
*/
|
||||
- (NSArray<ASIndexedNodeContext *> *)_populateFromDataSourceWithSectionIndexSet:(NSIndexSet *)indexSet
|
||||
- (NSArray<ASIndexedNodeContext *> *)_populateNodeContextsFromDataSourceForSections:(NSIndexSet *)sections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
@@ -460,7 +460,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
std::vector<NSInteger> counts = [self itemCountsFromDataSource];
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
[sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) {
|
||||
NSUInteger itemCount = counts[sectionIndex];
|
||||
for (NSUInteger i = 0; i < itemCount; i++) {
|
||||
@@ -556,7 +556,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateFromDataSourceWithSectionIndexSet:sections];
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateNodeContextsFromDataSourceForSections:sections];
|
||||
|
||||
[self prepareForInsertSections:sections];
|
||||
|
||||
@@ -824,7 +824,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
#pragma mark - Data Querying (Subclass API)
|
||||
|
||||
- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind
|
||||
- (NSArray<NSIndexPath *> *)indexPathsForEditingNodesOfKind:(NSString *)kind
|
||||
{
|
||||
NSArray *nodes = _editingNodes[kind];
|
||||
return nodes != nil ? ASIndexPathsForTwoDimensionalArray(nodes) : nil;
|
||||
|
||||
23
AsyncDisplayKit/Details/ASSectionContext.h
Normal file
23
AsyncDisplayKit/Details/ASSectionContext.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// ASSectionContext.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Huy Nguyen on 28/08/16.
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
@class ASCollectionView;
|
||||
|
||||
@protocol ASSectionContext
|
||||
|
||||
/**
|
||||
* Custom name of this section, for debugging only.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *sectionName;
|
||||
@property (nonatomic, weak, nullable) ASCollectionView *collectionView;
|
||||
|
||||
@end
|
||||
@@ -22,7 +22,7 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCellNode *> *nodes, NS
|
||||
/**
|
||||
* Provides a collection of index paths for nodes of the given kind that are currently in the editing store
|
||||
*/
|
||||
- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind;
|
||||
- (NSArray<NSIndexPath *> *)indexPathsForEditingNodesOfKind:(NSString *)kind;
|
||||
|
||||
/**
|
||||
* Read-only access to the underlying editing nodes of the given kind
|
||||
|
||||
25
AsyncDisplayKit/Private/ASSection.h
Normal file
25
AsyncDisplayKit/Private/ASSection.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// ASSection.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Huy Nguyen on 28/08/16.
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@protocol ASSectionContext;
|
||||
|
||||
@interface ASSection : NSObject
|
||||
|
||||
@property (nonatomic, assign, readonly) NSInteger sectionID;
|
||||
@property (nonatomic, strong, nullable, readonly) id<ASSectionContext> context;
|
||||
|
||||
- (nullable instancetype)init __unavailable;
|
||||
- (nullable instancetype)initWithSectionID:(NSInteger)sectionID context:(nullable id<ASSectionContext>)context NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
28
AsyncDisplayKit/Private/ASSection.m
Normal file
28
AsyncDisplayKit/Private/ASSection.m
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ASSection.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Huy Nguyen on 28/08/16.
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "ASSection.h"
|
||||
#import "ASSectionContext.h"
|
||||
|
||||
@implementation ASSection
|
||||
|
||||
- (instancetype)initWithSectionID:(NSInteger)sectionID context:(id<ASSectionContext>)context
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_sectionID = sectionID;
|
||||
_context = context;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "ASCellNode.h"
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASDisplayNode+Beta.h"
|
||||
#import "ASSectionContext.h"
|
||||
#import <vector>
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
@@ -34,8 +35,23 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTestSectionContext : NSObject <ASSectionContext>
|
||||
|
||||
@property (nonatomic, assign) NSInteger sectionIndex;
|
||||
@property (nonatomic, assign) NSInteger sectionGeneration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASTestSectionContext
|
||||
|
||||
@synthesize sectionName = _sectionName, collectionView = _collectionView;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewTestDelegate : NSObject <ASCollectionViewDataSource, ASCollectionViewDelegate>
|
||||
|
||||
@property (nonatomic, assign) NSInteger sectionGeneration;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASCollectionViewTestDelegate {
|
||||
@@ -48,6 +64,7 @@
|
||||
for (NSInteger i = 0; i < numberOfSections; i++) {
|
||||
_itemCounts.push_back(numberOfItemsInSection);
|
||||
}
|
||||
_sectionGeneration = 1;
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -77,6 +94,14 @@
|
||||
return _itemCounts[section];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)collectionView:(ASCollectionView *)collectionView contextForSection:(NSInteger)section
|
||||
{
|
||||
ASTestSectionContext *context = [[ASTestSectionContext alloc] init];
|
||||
context.sectionGeneration = _sectionGeneration;
|
||||
context.sectionIndex = section;
|
||||
return context;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewTestController: UIViewController
|
||||
@@ -474,4 +499,81 @@
|
||||
XCTAssertEqualObjects(completions, (@[ inner0, inner1, outer ]), @"Expected completion order to be correct");
|
||||
}
|
||||
|
||||
#pragma mark - ASSectionContext tests
|
||||
|
||||
- (void)testThatSectionContextsAreCorrectAfterTheInitialLayout
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 1);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testThatSectionContextsAreCorrectAfterSectionMove
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
NSInteger originalSection = sectionCount - 1;
|
||||
NSInteger toSection = 0;
|
||||
|
||||
del.sectionGeneration++;
|
||||
[cv moveSection:originalSection toSection:toSection];
|
||||
[cv waitUntilAllUpdatesAreCommitted];
|
||||
|
||||
// Only test left moving
|
||||
XCTAssertTrue(toSection < originalSection);
|
||||
ASTestSectionContext *movedSectionContext = (ASTestSectionContext *)[cv contextForSection:toSection];
|
||||
XCTAssertNotNil(movedSectionContext);
|
||||
// ASCollectionView currently uses ASChangeSetDataController which splits a move operation to a pair of delete and insert ones.
|
||||
// So this movedSectionContext is newly loaded and thus is second generation.
|
||||
XCTAssertEqual(movedSectionContext.sectionGeneration, 2);
|
||||
XCTAssertEqual(movedSectionContext.sectionIndex, toSection);
|
||||
|
||||
for (NSInteger section = toSection + 1; section <= originalSection && section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 1);
|
||||
// This section context was shifted to the right
|
||||
XCTAssertEqual(context.sectionIndex, (section - 1));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testThatSectionContextsAreCorrectAfterReloadData
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
|
||||
del.sectionGeneration++;
|
||||
[cv reloadDataImmediately];
|
||||
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 2);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testThatSectionContextsAreCorrectAfterReloadASection
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSInteger sectionToReload = 0;
|
||||
|
||||
del.sectionGeneration++;
|
||||
[cv reloadSections:[NSIndexSet indexSetWithIndex:sectionToReload]];
|
||||
[cv waitUntilAllUpdatesAreCommitted];
|
||||
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, section != sectionToReload ? 1 : 2);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Information about a section of items in a collection.
|
||||
*
|
||||
* This class is private to ASDK.
|
||||
* Data sources may override -collectionView:infoForSectionAtIndex: to provide the
|
||||
* "userInfo" object when the section is initially inserted. ASCollectionView
|
||||
* vends the user info object publicly via "infoForSectionAtIndex:".
|
||||
*/
|
||||
@interface ASCollectionSection : NSObject
|
||||
|
||||
// Autoincrementing value, set by collection view immediately after creation.
|
||||
@property NSInteger sectionID;
|
||||
|
||||
@property NSMutableDictionary<NSString *, NSMutableArray<ASCellNode *> *> *editingNodesByKind;
|
||||
@property NSMutableDictionary<NSString *, NSMutableArray<ASCellNode *> *> *completedNodesByKind;
|
||||
|
||||
@property (strong, nullable) id<ASSectionUserInfo> userInfo;
|
||||
|
||||
@end
|
||||
|
||||
@protocol ASSectionUserInfo
|
||||
// This will be set once, immediately after the object is returned by the data source.
|
||||
@property (weak, nonatomic, nullable) ASCollectionView *collectionView;
|
||||
|
||||
// Could be optional, but need to cache -respondsToSelector: dynamically.
|
||||
@property (nullable, readonly, copy) NSString *sectionName;
|
||||
@end
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* An example of what a section info class might look like.
|
||||
*/
|
||||
@interface ASExampleLayoutSectionInfo : NSObject <ASSectionUserInfo>
|
||||
|
||||
@property (nonatomic, weak, nullable) ASCollectionView *collectionView;
|
||||
@property (nullable, copy) NSString *sectionName;
|
||||
|
||||
@property CGSize cellSpacing;
|
||||
@property NSInteger numberOfColumns;
|
||||
@property CGFloat columnWidth;
|
||||
@property CGSize headerSize;
|
||||
@property CGSize footerSize;
|
||||
@property UIEdgeInsets headerInsets;
|
||||
@property UIEdgeInsets footerInsets;
|
||||
@property ASExampleLayoutBackgroundType backgroundType;
|
||||
@property ASExampleLayoutRowAlignmentType rowAlignmentType;
|
||||
@end
|
||||
|
||||
@implementation ASExampleLayoutSectionInfo
|
||||
|
||||
@end
|
||||
@@ -1,31 +0,0 @@
|
||||
// Added to ASCollectionDataSource:
|
||||
|
||||
/**
|
||||
* Data sources can override this method to return custom info associated with a
|
||||
* section of the collection view.
|
||||
*
|
||||
* These section info objects can be read by a UICollectionViewLayout subclass
|
||||
* and used to configure the layout for that section. @see ASSectionUserInfo
|
||||
*/
|
||||
@optional
|
||||
- (nullable id<ASSectionUserInfo>)collectionView:(ASCollectionView *)collectionView infoForSectionAtIndex:(NSInteger)sectionIndex;
|
||||
|
||||
// ----
|
||||
// Added to ASCollectionView:
|
||||
|
||||
// Reads from data controller's _completedSections. Asserts that section index is in bounds.
|
||||
- (nullable id<ASSectionUserInfo>)infoForSectionAtIndex:(NSInteger)sectionIndex;
|
||||
|
||||
// ----
|
||||
// In ASDataController.mm:
|
||||
|
||||
// Replace _editingNodes and _completedNodes with:
|
||||
NSMutableArray<ASCollectionSection *> *_editingSections;
|
||||
NSMutableArray<ASCollectionSection *> *_completedSections;
|
||||
|
||||
// Modify _reloadDataWithAnimationOptions and insertSections:withAnimationOptions:.
|
||||
// In those methods we use _populateFromDataSourceWithSectionIndexSet to get the node blocks.
|
||||
// Now we will also need to create the ASCollectionSections and ask for UserInfos, just before we get the node blocks.
|
||||
|
||||
// In essence, wherever we use an NSMutableArray of nodes to represent a section, we now
|
||||
// will use an ASCollectionSection instead.
|
||||
Reference in New Issue
Block a user