mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 07:05:35 +00:00
[Umbrella] ASCollectionView -> ASCollectionNode Migration, Separate Index Spaces (#2372)
* Separate dataSource & UIKit index spaces Beef up our supplementary node support Make the API way better Go nuts Add a unit test for UICollectionView's handling of reloadData inside batch updates Wrap indexPathForNode: in a cache Convert index paths in delegate methods Go back on table view Put collection view back Switch up the API Move most ASCollectionView API to ASCollectionNode Move most table logic over to ASTableNode Do the things More conversion work Keep on keepin' on Get table view delegate API done More porting Simplify Clear the delegate More cleanup Move more stuff around Remove pointless file Re-add some API Put back more API Use the right flag * Some cleanup * Remove incorrect comment * Tweak the API * Put back a couple methods * update example projects (note: ASCollectionView deprecation warnings expected) * change reloadDataWithCompletion:nil --> reloadData * Clean up rebase * Make deprecated numberOfItemsInSection methods optional * Use the right flag * Address nits * update ASDKTube, ASDKgram & ASViewController examples
This commit is contained in:
@@ -440,6 +440,7 @@
|
||||
CC7FD9DF1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */; };
|
||||
CC7FD9E11BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */; };
|
||||
CC7FD9E21BB603FF005CCB2B /* ASPhotosFrameworkImageRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
CC87BB951DA8193C0090E380 /* ASCellNode+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */; };
|
||||
CC88F7AE1D80AF5E000D6D4E /* ASObjectDescriptionHelpers.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = CC446A2D1D80AAE00071FD03 /* ASObjectDescriptionHelpers.h */; };
|
||||
CC8B05D61D73836400F54286 /* ASPerformanceTestContext.m in Sources */ = {isa = PBXBuildFile; fileRef = CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */; };
|
||||
CC8B05D81D73979700F54286 /* ASTextNodePerformanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */; };
|
||||
@@ -1106,6 +1107,7 @@
|
||||
BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
CC051F1E1D7A286A006434CB /* ASCALayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCALayerTests.m; sourceTree = "<group>"; };
|
||||
CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASUICollectionViewTests.m; sourceTree = "<group>"; };
|
||||
CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = "<group>"; };
|
||||
CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = "<group>"; };
|
||||
CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = "<group>"; };
|
||||
CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = "<group>"; };
|
||||
@@ -1119,11 +1121,13 @@
|
||||
CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+ASHelpers.m"; sourceTree = "<group>"; };
|
||||
CC4C2A751D88E3BF0039ACAB /* ASTraceEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTraceEvent.h; sourceTree = "<group>"; };
|
||||
CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTraceEvent.m; sourceTree = "<group>"; };
|
||||
CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableView+Undeprecated.h"; sourceTree = "<group>"; };
|
||||
CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = "<group>"; };
|
||||
CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDispatchTests.m; sourceTree = "<group>"; };
|
||||
CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = "<group>"; };
|
||||
CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = "<group>"; };
|
||||
CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = "<group>"; };
|
||||
CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASCellNode+Internal.h"; path = "AsyncDisplayKit/ASCellNode+Internal.h"; sourceTree = SOURCE_ROOT; };
|
||||
CC8B05D41D73836400F54286 /* ASPerformanceTestContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPerformanceTestContext.h; sourceTree = "<group>"; };
|
||||
CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPerformanceTestContext.m; sourceTree = "<group>"; };
|
||||
CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodePerformanceTests.m; sourceTree = "<group>"; };
|
||||
@@ -1540,6 +1544,8 @@
|
||||
2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */,
|
||||
044285051BAA63FE00D16268 /* ASBatchFetching.h */,
|
||||
044285061BAA63FE00D16268 /* ASBatchFetching.m */,
|
||||
CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */,
|
||||
CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */,
|
||||
251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */,
|
||||
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
|
||||
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
|
||||
@@ -1578,6 +1584,7 @@
|
||||
ACF6ED481B17847A00DA7C62 /* ASStackPositionedLayout.mm */,
|
||||
ACF6ED491B17847A00DA7C62 /* ASStackUnpositionedLayout.h */,
|
||||
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */,
|
||||
CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */,
|
||||
83A7D9581D44542100BF333E /* ASWeakMap.h */,
|
||||
83A7D9591D44542100BF333E /* ASWeakMap.m */,
|
||||
);
|
||||
@@ -1784,6 +1791,7 @@
|
||||
044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */,
|
||||
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
|
||||
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
||||
CC87BB951DA8193C0090E380 /* ASCellNode+Internal.h in Headers */,
|
||||
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
|
||||
9C70F20C1CDBE9B6007D6C76 /* ASCollectionDataController.h in Headers */,
|
||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
|
||||
@@ -58,6 +58,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes;
|
||||
|
||||
/// readwrite variant of the readonly public property.
|
||||
@property (nonatomic, copy, nullable) NSString *supplementaryElementKind;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -74,6 +74,13 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) {
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL neverShowPlaceholders;
|
||||
|
||||
/*
|
||||
* The kind of supplementary element this node represents, if any.
|
||||
*
|
||||
* @return The supplementary element kind, or @c nil if this node does not represent a supplementary element.
|
||||
*/
|
||||
@property (nonatomic, copy, readonly, nullable) NSString *supplementaryElementKind;
|
||||
|
||||
/*
|
||||
* The layout attributes currently assigned to this node, if any.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
#import <AsyncDisplayKit/_ASDisplayView.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
|
||||
@@ -298,8 +300,16 @@
|
||||
|
||||
ASDisplayNode *owningNode = scrollView.asyncdisplaykit_node;
|
||||
if ([owningNode isKindOfClass:[ASCollectionNode class]]) {
|
||||
NSIndexPath *ip = [(ASCollectionNode *)owningNode indexPathForNode:self];
|
||||
if (ip != nil) {
|
||||
[result addObject:@{ @"indexPath" : ip }];
|
||||
}
|
||||
[result addObject:@{ @"collectionNode" : ASObjectDescriptionMakeTiny(owningNode) }];
|
||||
} else if ([owningNode isKindOfClass:[ASTableNode class]]) {
|
||||
NSIndexPath *ip = [(ASTableNode *)owningNode indexPathForNode:self];
|
||||
if (ip != nil) {
|
||||
[result addObject:@{ @"indexPath" : ip }];
|
||||
}
|
||||
[result addObject:@{ @"tableNode" : ASObjectDescriptionMakeTiny(owningNode) }];
|
||||
|
||||
} else if ([scrollView isKindOfClass:[ASCollectionView class]]) {
|
||||
|
||||
@@ -17,11 +17,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator;
|
||||
|
||||
- (void)beginUpdates;
|
||||
- (void)beginUpdates ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated;
|
||||
- (void)endUpdatesAnimated:(BOOL)animated ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion;
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -122,6 +122,127 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
#pragma mark - Editing
|
||||
|
||||
/**
|
||||
* Registers the given kind of supplementary node for use in creating node-backed supplementary elements.
|
||||
*
|
||||
* @param elementKind The kind of supplementary node that will be requested through the data source.
|
||||
*
|
||||
* @discussion Use this method to register support for the use of supplementary nodes in place of the default
|
||||
* `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:`
|
||||
* methods. This method will register an internal backing view that will host the contents of the supplementary nodes
|
||||
* returned from the data source.
|
||||
*/
|
||||
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The data source must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param animated NO to disable animations for this batch
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The data source must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Inserts items at the locations identified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Deletes the items specified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Reloads the specified items.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Moves the item at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the item to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The data source must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
@@ -131,6 +252,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion;
|
||||
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
@@ -138,13 +260,337 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
#pragma mark - Querying Data
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
* Retrieves the number of items in the given section.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
||||
* while all the cells load.
|
||||
* @param section The section.
|
||||
*
|
||||
* @return The number of items.
|
||||
*/
|
||||
- (void)reloadDataImmediately;
|
||||
- (NSInteger)numberOfItemsInSection:(NSInteger)section AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* The number of sections.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSInteger numberOfSections;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Retrieves the node for the item at the given index path.
|
||||
*
|
||||
* @param indexPath The index path of the requested item.
|
||||
*
|
||||
* @return The node for the given item, or @c nil if no item exists at the specified path.
|
||||
*/
|
||||
- (nullable ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Retrieve the index path for the item with the given node.
|
||||
*
|
||||
* @param cellNode A node for an item in the collection node.
|
||||
*
|
||||
* @return The indexPath for this item.
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the context object for the given section, as provided by the data source in
|
||||
* the @c collectionNode:contextForSection: method.
|
||||
*
|
||||
* @param section The section to get the context for.
|
||||
*
|
||||
* @return The context object, or @c nil if no context was provided.
|
||||
*
|
||||
* TODO: This method currently accepts @c section in the _view_ index space, but it should
|
||||
* be in the node index space. To get the context in the view index space (e.g. for subclasses
|
||||
* of @c UICollectionViewLayout, the user will call the same method on @c ASCollectionView.
|
||||
*/
|
||||
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionNode (Deprecated)
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*
|
||||
* @deprecated This method is deprecated in 2.0. Use @c reloadDataWithCompletion: and
|
||||
* then @c waitUntilAllUpdatesAreCommitted instead.
|
||||
*/
|
||||
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UICollectionViewDataSource.
|
||||
*/
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionDataSource>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Asks the data source for the number of items in the given section of the collection node.
|
||||
*
|
||||
* @see @c collectionView:numberOfItemsInSection:
|
||||
*/
|
||||
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section;
|
||||
|
||||
/**
|
||||
* Asks the data source for the number of sections in the collection node.
|
||||
*
|
||||
* @see @c numberOfSectionsInCollectionView:
|
||||
*/
|
||||
- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode;
|
||||
|
||||
/**
|
||||
* Similar to -collectionNode:nodeForItemAtIndexPath:
|
||||
* This method takes precedence over collectionNode:nodeForItemAtIndexPath: if implemented.
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
* @param indexPath The index path of the item.
|
||||
*
|
||||
* @return a block that creates the node for display for this item.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
*/
|
||||
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param indexPath The index path of the item.
|
||||
*
|
||||
* @return A node to display for the given item. This will be called on the main thread and should
|
||||
* not implement reuse (it will be called once per item). Unlike UICollectionView's version,
|
||||
* this method is not called when the item is about to display.
|
||||
*/
|
||||
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Asks the data source to provide a node to display for the given supplementary element in the collection view.
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
* @param kind The kind of supplementary element.
|
||||
* @param indexPath The index path of the supplementary element.
|
||||
*/
|
||||
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Asks the data source to provide a context object for the given section. This object
|
||||
* can later be retrieved by calling @c contextForSection: and is useful when implementing
|
||||
* custom @c UICollectionViewLayout subclasses. The context object is ret
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
* @param section The index of the section to provide context for.
|
||||
*
|
||||
* @return A context object to assign to the given section, or @c nil.
|
||||
*/
|
||||
- (nullable id<ASSectionContext>)collectionNode:(ASCollectionNode *)collectionNode contextForSection:(NSInteger)section;
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath. This will be called on the main thread and should
|
||||
* not implement reuse (it will be called once per row). Unlike UICollectionView's version,
|
||||
* this method is not called when the row is about to display.
|
||||
*/
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:nodeForItemAtIndexPath:
|
||||
* This method takes precedence over collectionView:nodeForItemAtIndexPath: if implemented.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a block that creates the node for display at this indexpath.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
*/
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Asks the collection view to provide a supplementary node to display in the collection view.
|
||||
*
|
||||
* @param collectionView An object representing the collection view requesting this information.
|
||||
* @param kind The kind of supplementary node to provide.
|
||||
* @param indexPath The index path that specifies the location of the new supplementary node.
|
||||
*/
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UICollectionViewDelegate.
|
||||
*/
|
||||
@protocol ASCollectionDelegate <ASCommonCollectionDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the given item.
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
*
|
||||
* @param indexPath The index path of the item.
|
||||
*
|
||||
* @return A constrained size range for layout for the item at this index path.
|
||||
*/
|
||||
- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode willDisplayItemWithNode:(ASCellNode *)node;
|
||||
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didEndDisplayingItemWithNode:(ASCellNode *)node;
|
||||
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode willDisplaySupplementaryElementWithNode:(ASCellNode *)node NS_AVAILABLE_IOS(8_0);
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didEndDisplayingSupplementaryElementWithNode:(ASCellNode *)node;
|
||||
|
||||
- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (BOOL)collectionNode:(ASCollectionNode *)collectionNode shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionNode:(ASCollectionNode *)collectionNode canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath sender:(nullable id)sender;
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath sender:(nullable id)sender;
|
||||
|
||||
/**
|
||||
* Receive a message that the collection node is near the end of its data set and more data should be fetched if
|
||||
* necessary.
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
* @param context A context object that must be notified when the batch fetch is completed.
|
||||
*
|
||||
* @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future
|
||||
* notifications to do batch fetches. This method is called on a background queue.
|
||||
*
|
||||
* ASCollectionNode currently only supports batch events for tail loads. If you require a head load, consider
|
||||
* implementing a UIRefreshControl.
|
||||
*/
|
||||
- (void)collectionNode:(ASCollectionNode *)collectionNode willBeginBatchFetchWithContext:(ASBatchContext *)context;
|
||||
|
||||
/**
|
||||
* Tell the collection node if batch fetching should begin.
|
||||
*
|
||||
* @param collectionNode The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the collection node assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForCollectionNode:(ASCollectionNode *)collectionNode;
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the node at the index path.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @return A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view will add the given node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param node The node that will be displayed.
|
||||
* @param indexPath The index path of the item that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view did remove the provided node from the view hierarchy.
|
||||
* This may be caused by the node scrolling out of view, or by deleting the item
|
||||
* or its containing section with @c deleteItemsAtIndexPaths: or @c deleteSections: .
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param node The node which was removed from the view hierarchy.
|
||||
* @param indexPath The index path at which the node was located before it was removed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Tell the collectionView if batch fetching should begin.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view will add the node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param indexPath The index path of the item that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*
|
||||
* This method is deprecated. Use @c collectionView:willDisplayNode:forItemAtIndexPath: instead.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
#import "AsyncDisplayKit+Debug.h"
|
||||
#import "ASSectionContext.h"
|
||||
#import "ASCollectionDataController.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
|
||||
#pragma mark - _ASCollectionPendingState
|
||||
|
||||
@@ -106,18 +109,6 @@
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithCollectionView:(ASCollectionView *)collectionView
|
||||
{
|
||||
ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{ return collectionView; };
|
||||
|
||||
if (self = [super initWithViewBlock:collectionViewBlock]) {
|
||||
// ASCollectionView created directly by the app. Trigger -loadView to set up collectionNode pointer.
|
||||
__unused ASCollectionView *collectionView = [self view];
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil];
|
||||
@@ -126,15 +117,21 @@
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator
|
||||
{
|
||||
ASDisplayNodeViewBlock collectionViewBlock = ^UIView *{
|
||||
return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:layoutFacilitator ownedByNode:YES];
|
||||
return [[ASCollectionView alloc] _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:layoutFacilitator];
|
||||
};
|
||||
|
||||
|
||||
if (self = [super initWithViewBlock:collectionViewBlock]) {
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
self.delegate = nil;
|
||||
self.dataSource = nil;
|
||||
}
|
||||
|
||||
#pragma mark ASDisplayNode
|
||||
|
||||
- (void)didLoad
|
||||
@@ -149,6 +146,7 @@
|
||||
self.pendingState = nil;
|
||||
view.asyncDelegate = pendingState.delegate;
|
||||
view.asyncDataSource = pendingState.dataSource;
|
||||
|
||||
if (pendingState.rangeMode != ASLayoutRangeModeCount) {
|
||||
[view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode];
|
||||
}
|
||||
@@ -163,13 +161,13 @@
|
||||
- (void)clearContents
|
||||
{
|
||||
[super clearContents];
|
||||
[self.view clearContents];
|
||||
[self.rangeController clearContents];
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
{
|
||||
[super clearFetchedData];
|
||||
[self.view clearFetchedData];
|
||||
[self.rangeController clearFetchedData];
|
||||
}
|
||||
|
||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||
@@ -194,6 +192,18 @@
|
||||
|
||||
#pragma mark Setter / Getter
|
||||
|
||||
// TODO: Implement this without the view.
|
||||
- (ASCollectionDataController *)dataController
|
||||
{
|
||||
return (ASCollectionDataController *)self.view.dataController;
|
||||
}
|
||||
|
||||
// TODO: Implement this without the view.
|
||||
- (ASRangeController *)rangeController
|
||||
{
|
||||
return self.view.rangeController;
|
||||
}
|
||||
|
||||
- (_ASCollectionPendingState *)pendingState
|
||||
{
|
||||
if (!_pendingState && ![self isNodeLoaded]) {
|
||||
@@ -241,26 +251,76 @@
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark ASCollectionView Forwards
|
||||
#pragma mark - Range Tuning
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.view.rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
return [self.rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
[self.view.rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
[self.rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.view.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.view.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType];
|
||||
return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
#pragma mark - Querying Data
|
||||
|
||||
- (NSInteger)numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return [self.dataController numberOfRowsInSection:section];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSections
|
||||
{
|
||||
return [self.dataController numberOfSections];
|
||||
}
|
||||
|
||||
- (NSArray<__kindof ASCellNode *> *)visibleNodes
|
||||
{
|
||||
return [self.view visibleNodes];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
return [self.dataController indexPathForNode:cellNode];
|
||||
}
|
||||
|
||||
- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [self.dataController nodeAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)contextForSection:(NSInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return [self.dataController contextForSection:section];
|
||||
}
|
||||
|
||||
#pragma mark - Editing
|
||||
|
||||
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind
|
||||
{
|
||||
[self.view registerSupplementaryNodeOfKind:elementKind];
|
||||
}
|
||||
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self.view performBatchAnimated:animated updates:updates completion:completion];
|
||||
}
|
||||
|
||||
- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self.view performBatchUpdates:updates completion:completion];
|
||||
}
|
||||
|
||||
- (void)reloadDataWithCompletion:(void (^)())completion
|
||||
@@ -280,7 +340,7 @@
|
||||
|
||||
- (void)beginUpdates
|
||||
{
|
||||
[self.view.dataController beginUpdates];
|
||||
[self.dataController beginUpdates];
|
||||
}
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated
|
||||
@@ -290,7 +350,47 @@
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self.view.dataController endUpdatesAnimated:animated completion:completion];
|
||||
[self.dataController endUpdatesAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections
|
||||
{
|
||||
[self.view insertSections:sections];
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections
|
||||
{
|
||||
[self.view deleteSections:sections];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections
|
||||
{
|
||||
[self.view reloadSections:sections];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
|
||||
{
|
||||
[self.view moveSection:section toSection:newSection];
|
||||
}
|
||||
|
||||
- (void)insertItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
[self.view insertItemsAtIndexPaths:indexPaths];
|
||||
}
|
||||
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
[self.view deleteItemsAtIndexPaths:indexPaths];
|
||||
}
|
||||
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
[self.view reloadItemsAtIndexPaths:indexPaths];
|
||||
}
|
||||
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
|
||||
{
|
||||
[self.view moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
|
||||
}
|
||||
|
||||
#pragma mark - ASRangeControllerUpdateRangeProtocol
|
||||
@@ -300,7 +400,7 @@
|
||||
if ([self pendingState]) {
|
||||
_pendingState.rangeMode = rangeMode;
|
||||
} else {
|
||||
[self.view.rangeController updateCurrentRangeWithMode:rangeMode];
|
||||
[self.rangeController updateCurrentRangeWithMode:rangeMode];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,32 +38,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface ASCollectionView : UICollectionView
|
||||
|
||||
/**
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified layout.
|
||||
*
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
/**
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified frame and layout.
|
||||
*
|
||||
* @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization.
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
/**
|
||||
* Returns the corresponding ASCollectionNode
|
||||
*
|
||||
* @return collectionNode The corresponding ASCollectionNode which exists even if directly allocating & handling the view class.
|
||||
*/
|
||||
@property (nonatomic, weak, readonly) ASCollectionNode *collectionNode;
|
||||
|
||||
/**
|
||||
* The object that acts as the asynchronous delegate of the collection view
|
||||
*
|
||||
@@ -83,52 +57,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, weak) id<ASCollectionDataSource> asyncDataSource;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in full mode.
|
||||
* Returns the corresponding ASCollectionNode
|
||||
*
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in full mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
* @return collectionNode The corresponding ASCollectionNode, if one exists.
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param rangeMode The range mode to get the running parameters for.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in the given mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeMode The range mode to set the running parameters for.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
@property (nonatomic, weak, readonly) ASCollectionNode *collectionNode;
|
||||
|
||||
/**
|
||||
* The number of screens left to scroll before the delegate -collectionView:beginBatchFetchingWithContext: is called.
|
||||
@@ -150,204 +83,32 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutInspector;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param animated NO to disable animations for this batch
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before update block completes.
|
||||
*
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
||||
* while all the cells load.
|
||||
*/
|
||||
- (void)reloadDataImmediately;
|
||||
|
||||
/**
|
||||
* Triggers a relayout of all nodes.
|
||||
*
|
||||
* @discussion This method invalidates and lays out every cell node in the collection view.
|
||||
*/
|
||||
- (void)relayoutItems;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted;
|
||||
|
||||
/**
|
||||
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
|
||||
*
|
||||
* @param elementKind The kind of supplementary node that will be requested through the data source.
|
||||
*
|
||||
* @discussion Use this method to register support for the use of supplementary nodes in place of the default
|
||||
* `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:`
|
||||
* methods. This method will register an internal backing view that will host the contents of the supplementary nodes
|
||||
* returned from the data source.
|
||||
*/
|
||||
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Inserts items at the locations identified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Deletes the items specified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Reloads the specified items.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Moves the item at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the item to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
|
||||
/**
|
||||
* Similar to -cellForItemAtIndexPath:.
|
||||
* Retrieves the node for the item at the given index path.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath or nil
|
||||
* @return The node at the given index path, or @c nil if no item exists at the specified path.
|
||||
*/
|
||||
- (nullable ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
/**
|
||||
* Similar to -supplementaryViewForElementKind:atIndexPath:
|
||||
*
|
||||
* @param elementKind The kind of supplementary node to locate.
|
||||
* @param indexPath The index path of the requested supplementary node.
|
||||
*
|
||||
* @return The specified supplementary node or nil
|
||||
* @return The specified supplementary node or @c nil.
|
||||
*/
|
||||
- (nullable ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
* Retrieves the context object for the given section, as provided by the data source in
|
||||
* the @c collectionNode:contextForSection: method. This method must be called on the main thread.
|
||||
*
|
||||
* @param cellNode a cellNode part of the table view
|
||||
* @param section The section to get the context for.
|
||||
*
|
||||
* @return an indexPath for this cellNode
|
||||
* @return The context object, or @c nil if no context was provided.
|
||||
*/
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Query the sized node at `indexPath` for its calculatedSize.
|
||||
*
|
||||
* @param indexPath The index path for the node of interest.
|
||||
*/
|
||||
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Determines collection view's current scroll direction. Supports 2-axis collection views.
|
||||
@@ -363,20 +124,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, readonly) ASScrollDirection scrollableDirections;
|
||||
|
||||
/**
|
||||
* Triggers all loaded ASCellNodes to destroy displayed contents (freeing a lot of memory).
|
||||
*
|
||||
* @discussion This method should only be called by ASCollectionNode. To be removed in a later release.
|
||||
*/
|
||||
- (void)clearContents;
|
||||
|
||||
/**
|
||||
* Triggers all loaded ASCellNodes to purge any data fetched from the network or disk (freeing memory).
|
||||
*
|
||||
* @discussion This method should only be called by ASCollectionNode. To be removed in a later release.
|
||||
*/
|
||||
- (void)clearFetchedData;
|
||||
|
||||
/**
|
||||
* Forces the .contentInset to be UIEdgeInsetsZero.
|
||||
*
|
||||
@@ -389,173 +136,274 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionView (Deprecated)
|
||||
|
||||
/**
|
||||
* This is a node-based UICollectionViewDataSource.
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified layout.
|
||||
*
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
#define ASCollectionViewDataSource ASCollectionDataSource
|
||||
@protocol ASCollectionDataSource <ASCommonCollectionViewDataSource>
|
||||
|
||||
@optional
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:cellForItemAtIndexPath:.
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified frame and layout.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath. This will be called on the main thread and should
|
||||
* not implement reuse (it will be called once per row). Unlike UICollectionView's version,
|
||||
* this method is not called when the row is about to display.
|
||||
* @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization.
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -collectionView:nodeForItemAtIndexPath:
|
||||
* This method takes precedence over collectionView:nodeForItemAtIndexPath: if implemented.
|
||||
* Tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
* @return A tuning parameter value for the given range type in full mode.
|
||||
*
|
||||
* @return a block that creates the node for display at this indexpath.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Asks the collection view to provide a supplementary node to display in the collection view.
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param collectionView An object representing the collection view requesting this information.
|
||||
* @param kind The kind of supplementary node to provide.
|
||||
* @param indexPath The index path that specifies the location of the new supplementary node.
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
* @param rangeMode The range mode to get the running parameters for.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in the given mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)collectionViewLockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeMode The range mode to set the running parameters for.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)collectionViewUnlockDataSource:(ASCollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (nullable id<ASSectionContext>)collectionView:(ASCollectionView *)collectionView contextForSection:(NSInteger)section;
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param animated NO to disable animations for this batch
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before update block completes.
|
||||
*
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadData ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
||||
* while all the cells load.
|
||||
*/
|
||||
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Triggers a relayout of all nodes.
|
||||
*
|
||||
* @discussion This method invalidates and lays out every cell node in the collection.
|
||||
*/
|
||||
- (void)relayoutItems ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
|
||||
*
|
||||
* @param elementKind The kind of supplementary node that will be requested through the data source.
|
||||
*
|
||||
* @discussion Use this method to register support for the use of supplementary nodes in place of the default
|
||||
* `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:`
|
||||
* methods. This method will register an internal backing view that will host the contents of the supplementary nodes
|
||||
* returned from the data source.
|
||||
*/
|
||||
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Inserts items at the locations identified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Deletes the items specified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reloads the specified items.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Moves the item at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the item to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Query the sized node at @c indexPath for its calculatedSize.
|
||||
*
|
||||
* @param indexPath The index path for the node of interest.
|
||||
*
|
||||
* This method is deprecated. Call @c calculatedSize on the node of interest instead. First deprecated in version 2.0.
|
||||
*/
|
||||
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a cellNode in the collection view
|
||||
*
|
||||
* @return The index path for this cell node.
|
||||
*
|
||||
* @discussion This index path returned by this method is in the _view's_ index space
|
||||
* and should only be used with @c ASCollectionView directly. To get an index path suitable
|
||||
* for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the
|
||||
* collection node instead.
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
ASDISPLAYNODE_DEPRECATED
|
||||
@protocol ASCollectionViewDataSource <ASCollectionDataSource>
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UICollectionViewDelegate.
|
||||
*/
|
||||
#define ASCollectionViewDelegate ASCollectionDelegate
|
||||
@protocol ASCollectionDelegate <ASCommonCollectionViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the node at the index path.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @return A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view will add the given node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param node The node that will be displayed.
|
||||
* @param indexPath The index path of the item that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view did remove the provided node from the view hierarchy.
|
||||
* This may be caused by the node scrolling out of view, or by deleting the item
|
||||
* or its containing section with @c deleteItemsAtIndexPaths: or @c deleteSections: .
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param node The node which was removed from the view hierarchy.
|
||||
* @param indexPath The index path at which the node was located before it was removed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView didEndDisplayingNode:(ASCellNode *)node forItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Receive a message that the collectionView is near the end of its data set and more data should be fetched if
|
||||
* necessary.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param context A context object that must be notified when the batch fetch is completed.
|
||||
*
|
||||
* @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future
|
||||
* notifications to do batch fetches. This method is called on a background queue.
|
||||
*
|
||||
* UICollectionView currently only supports batch events for tail loads. If you require a head load, consider
|
||||
* implementing a UIRefreshControl.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willBeginBatchFetchWithContext:(ASBatchContext *)context;
|
||||
|
||||
/**
|
||||
* Tell the collectionView if batch fetching should begin.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the collectionView assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForCollectionView:(ASCollectionView *)collectionView;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the collection view will add the node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param collectionView The sender.
|
||||
* @param indexPath The index path of the item that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes collection view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*
|
||||
* This method is deprecated. Use @c collectionView:willDisplayNode:forItemAtIndexPath: instead.
|
||||
*/
|
||||
- (void)collectionView:(ASCollectionView *)collectionView willDisplayNodeForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
ASDISPLAYNODE_DEPRECATED
|
||||
@protocol ASCollectionViewDelegate <ASCollectionDelegate>
|
||||
@end
|
||||
|
||||
/**
|
||||
* Defines methods that let you coordinate with a `UICollectionViewFlowLayout` in combination with an `ASCollectionView`.
|
||||
*/
|
||||
@protocol ASCollectionViewDelegateFlowLayout <ASCollectionViewDelegate>
|
||||
@protocol ASCollectionViewDelegateFlowLayout <ASCollectionDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "ASCollectionViewLayoutFacilitatorProtocol.h"
|
||||
#import "ASSectionContext.h"
|
||||
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
|
||||
/// What, if any, invalidation should we perform during the next -layoutSubviews.
|
||||
typedef NS_ENUM(NSUInteger, ASCollectionViewInvalidationStyle) {
|
||||
@@ -157,38 +157,65 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
BOOL _superIsPendingDataLoad;
|
||||
|
||||
struct {
|
||||
unsigned int asyncDelegateScrollViewDidScroll:1;
|
||||
unsigned int asyncDelegateScrollViewWillBeginDragging:1;
|
||||
unsigned int asyncDelegateScrollViewDidEndDragging:1;
|
||||
unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1;
|
||||
unsigned int asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath:1;
|
||||
unsigned int asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPathDeprecated:1;
|
||||
unsigned int asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath:1;
|
||||
unsigned int asyncDelegateCollectionViewWillBeginBatchFetchWithContext:1;
|
||||
unsigned int asyncDelegateShouldBatchFetchForCollectionView:1;
|
||||
unsigned int scrollViewDidScroll:1;
|
||||
unsigned int scrollViewWillBeginDragging:1;
|
||||
unsigned int scrollViewDidEndDragging:1;
|
||||
unsigned int scrollViewWillEndDragging:1;
|
||||
unsigned int collectionViewWillDisplayNodeForItem:1;
|
||||
unsigned int collectionViewWillDisplayNodeForItemDeprecated:1;
|
||||
unsigned int collectionViewDidEndDisplayingNodeForItem:1;
|
||||
unsigned int collectionViewShouldSelectItem:1;
|
||||
unsigned int collectionViewDidSelectItem:1;
|
||||
unsigned int collectionViewShouldDeselectItem:1;
|
||||
unsigned int collectionViewDidDeselectItem:1;
|
||||
unsigned int collectionViewShouldHighlightItem:1;
|
||||
unsigned int collectionViewDidHighlightItem:1;
|
||||
unsigned int collectionViewDidUnhighlightItem:1;
|
||||
unsigned int collectionViewShouldShowMenuForItem:1;
|
||||
unsigned int collectionViewCanPerformActionForItem:1;
|
||||
unsigned int collectionViewPerformActionForItem:1;
|
||||
unsigned int collectionViewWillBeginBatchFetch:1;
|
||||
unsigned int shouldBatchFetchForCollectionView:1;
|
||||
unsigned int collectionNodeWillDisplayItem:1;
|
||||
unsigned int collectionNodeDidEndDisplayingItem:1;
|
||||
unsigned int collectionNodeShouldSelectItem:1;
|
||||
unsigned int collectionNodeDidSelectItem:1;
|
||||
unsigned int collectionNodeShouldDeselectItem:1;
|
||||
unsigned int collectionNodeDidDeselectItem:1;
|
||||
unsigned int collectionNodeShouldHighlightItem:1;
|
||||
unsigned int collectionNodeDidHighlightItem:1;
|
||||
unsigned int collectionNodeDidUnhighlightItem:1;
|
||||
unsigned int collectionNodeShouldShowMenuForItem:1;
|
||||
unsigned int collectionNodeCanPerformActionForItem:1;
|
||||
unsigned int collectionNodePerformActionForItem:1;
|
||||
unsigned int collectionNodeWillBeginBatchFetch:1;
|
||||
unsigned int collectionNodeWillDisplaySupplementaryElement:1;
|
||||
unsigned int collectionNodeDidEndDisplayingSupplementaryElement:1;
|
||||
|
||||
unsigned int shouldBatchFetchForCollectionNode:1;
|
||||
} _asyncDelegateFlags;
|
||||
|
||||
struct {
|
||||
unsigned int asyncDataSourceNodeForItemAtIndexPath:1;
|
||||
unsigned int asyncDataSourceNodeBlockForItemAtIndexPath:1;
|
||||
unsigned int asyncDataSourceNumberOfSectionsInCollectionView:1;
|
||||
unsigned int asyncDataSourceContextForSection:1;
|
||||
unsigned int collectionViewNodeForItem:1;
|
||||
unsigned int collectionViewNodeBlockForItem:1;
|
||||
unsigned int collectionViewNodeForSupplementaryElement:1;
|
||||
unsigned int numberOfSectionsInCollectionView:1;
|
||||
unsigned int collectionViewNumberOfItemsInSection:1;
|
||||
unsigned int collectionNodeNodeForItem:1;
|
||||
unsigned int collectionNodeNodeBlockForItem:1;
|
||||
unsigned int collectionNodeNodeForSupplementaryElement:1;
|
||||
unsigned int numberOfSectionsInCollectionNode:1;
|
||||
unsigned int collectionNodeNumberOfItemsInSection:1;
|
||||
unsigned int collectionNodeContextForSection:1;
|
||||
} _asyncDataSourceFlags;
|
||||
|
||||
struct {
|
||||
unsigned int layoutInspectorDidChangeCollectionViewDataSource:1;
|
||||
unsigned int layoutInspectorDidChangeCollectionViewDelegate:1;
|
||||
unsigned int layoutInspectorScrollableDirections:1;
|
||||
unsigned int didChangeCollectionViewDataSource:1;
|
||||
unsigned int didChangeCollectionViewDelegate:1;
|
||||
unsigned int scrollableDirections:1;
|
||||
} _layoutInspectorFlags;
|
||||
}
|
||||
|
||||
// Used only when ASCollectionView is created directly rather than through ASCollectionNode.
|
||||
// We create a node so that logic related to appearance, memory management, etc can be located there
|
||||
// for both the node-based and view-based version of the table.
|
||||
// This also permits sharing logic with ASTableNode, as the superclass is not UIKit-controlled.
|
||||
@property (nonatomic, strong) ASCollectionNode *strongCollectionNode;
|
||||
|
||||
// Always set, whether ASCollectionView is created directly or via ASCollectionNode.
|
||||
@property (nonatomic, weak) ASCollectionNode *collectionNode;
|
||||
|
||||
@end
|
||||
@@ -198,6 +225,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
@end
|
||||
|
||||
@implementation ASCollectionView
|
||||
@synthesize asyncDelegate = _asyncDelegate;
|
||||
@synthesize asyncDataSource = _asyncDataSource;
|
||||
|
||||
// Using _ASDisplayLayer ensures things like -layout are properly forwarded to ASCollectionNode.
|
||||
+ (Class)layerClass
|
||||
@@ -210,33 +239,19 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self _initWithFrame:CGRectZero collectionViewLayout:layout ownedByNode:NO];
|
||||
return [self initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout
|
||||
{
|
||||
return [self _initWithFrame:frame collectionViewLayout:layout ownedByNode:NO];
|
||||
return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout ownedByNode:(BOOL)ownedByNode
|
||||
{
|
||||
return [self _initWithFrame:frame collectionViewLayout:layout layoutFacilitator:nil ownedByNode:ownedByNode];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator ownedByNode:(BOOL)ownedByNode
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator
|
||||
{
|
||||
if (!(self = [super initWithFrame:frame collectionViewLayout:layout]))
|
||||
return nil;
|
||||
|
||||
if (!ownedByNode) {
|
||||
// See commentary at the definition of .strongCollectionNode for why we create an ASCollectionNode.
|
||||
// FIXME: The _view pointer of the node retains us, but the node will die immediately if we don't
|
||||
// retain it. At the moment there isn't a great solution to this, so we can't yet move our core
|
||||
// logic to ASCollectionNode (required to have a shared superclass with ASTable*).
|
||||
ASCollectionNode *collectionNode = nil; //[[ASCollectionNode alloc] _initWithCollectionView:self];
|
||||
self.strongCollectionNode = collectionNode;
|
||||
}
|
||||
|
||||
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self];
|
||||
|
||||
_rangeController = [[ASRangeController alloc] init];
|
||||
@@ -349,7 +364,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAsyncDataSource:(id<ASCollectionViewDataSource>)asyncDataSource
|
||||
- (void)setAsyncDataSource:(id<ASCollectionDataSource>)asyncDataSource
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||
@@ -366,23 +381,35 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
|
||||
_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:)];
|
||||
_asyncDataSourceFlags.collectionViewNodeForItem = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.collectionViewNodeBlockForItem = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeBlockForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.numberOfSectionsInCollectionView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)];
|
||||
_asyncDataSourceFlags.collectionViewNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionView:numberOfItemsInSection:)];
|
||||
_asyncDataSourceFlags.collectionViewNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionView:nodeForSupplementaryElementOfKind:atIndexPath:)];
|
||||
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath
|
||||
|| _asyncDataSourceFlags.asyncDataSourceNodeForItemAtIndexPath, @"Data source must implement collectionView:nodeForItemAtIndexPath: or collectionView:nodeBlockForItemAtIndexPath:");
|
||||
_asyncDataSourceFlags.collectionNodeNodeForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.collectionNodeNodeBlockForItem = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForItemAtIndexPath:)];
|
||||
_asyncDataSourceFlags.numberOfSectionsInCollectionNode = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInCollectionNode:)];
|
||||
_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:numberOfItemsInSection:)];
|
||||
_asyncDataSourceFlags.collectionNodeContextForSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:contextForSection:)];
|
||||
_asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:)];
|
||||
|
||||
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection || _asyncDataSourceFlags.collectionViewNumberOfItemsInSection, @"Data source must implement collectionNode:numberOfItemsInSection:");
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.collectionNodeNodeBlockForItem
|
||||
|| _asyncDataSourceFlags.collectionNodeNodeForItem
|
||||
|| _asyncDataSourceFlags.collectionViewNodeBlockForItem
|
||||
|| _asyncDataSourceFlags.collectionViewNodeForItem, @"Data source must implement collectionNode:nodeBlockForItemAtIndexPath: or collectionNode:nodeForItemAtIndexPath:");
|
||||
}
|
||||
|
||||
super.dataSource = (id<UICollectionViewDataSource>)_proxyDataSource;
|
||||
|
||||
if (_layoutInspectorFlags.layoutInspectorDidChangeCollectionViewDataSource) {
|
||||
if (_layoutInspectorFlags.didChangeCollectionViewDataSource) {
|
||||
[self.layoutInspector didChangeCollectionViewDataSource:asyncDataSource];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAsyncDelegate:(id<ASCollectionViewDelegate>)asyncDelegate
|
||||
- (void)setAsyncDelegate:(id<ASCollectionDelegate>)asyncDelegate
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||
@@ -399,22 +426,46 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
_asyncDelegate = asyncDelegate;
|
||||
_proxyDelegate = [[ASCollectionViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
|
||||
_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNode:forItemAtIndexPath:)];
|
||||
if (_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath == NO) {
|
||||
_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
|
||||
_asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
|
||||
_asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
|
||||
_asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
|
||||
_asyncDelegateFlags.collectionViewWillDisplayNodeForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNode:forItemAtIndexPath:)];
|
||||
if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItem == NO) {
|
||||
_asyncDelegateFlags.collectionViewWillDisplayNodeForItemDeprecated = [_asyncDelegate respondsToSelector:@selector(collectionView:willDisplayNodeForItemAtIndexPath:)];
|
||||
}
|
||||
_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNode:forItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
|
||||
_asyncDelegateFlags.collectionViewDidEndDisplayingNodeForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didEndDisplayingNode:forItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(collectionView:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.shouldBatchFetchForCollectionView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionView:)];
|
||||
_asyncDelegateFlags.collectionViewShouldSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldSelectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewDidSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didSelectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewShouldDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldDeselectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewDidDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didDeselectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewShouldHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldHighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewDidHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didHighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewDidUnhighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionView:didUnhighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewShouldShowMenuForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:shouldShowMenuForItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionViewCanPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:canPerformAction:forItemAtIndexPath:withSender:)];
|
||||
_asyncDelegateFlags.collectionViewPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionView:performAction:forItemAtIndexPath:withSender:)];
|
||||
_asyncDelegateFlags.collectionNodeWillDisplayItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:willDisplayItemWithNode:)];
|
||||
_asyncDelegateFlags.collectionNodeDidEndDisplayingItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didEndDisplayingItemWithNode:)];
|
||||
_asyncDelegateFlags.collectionNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(collectionNode:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.shouldBatchFetchForCollectionNode = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForCollectionNode:)];
|
||||
_asyncDelegateFlags.collectionNodeShouldSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldSelectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeDidSelectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didSelectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeShouldDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldDeselectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeDidDeselectItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didDeselectItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeShouldHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldHighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeDidHighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didHighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeDidUnhighlightItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:didUnhighlightItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeShouldShowMenuForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:shouldShowMenuForItemAtIndexPath:)];
|
||||
_asyncDelegateFlags.collectionNodeCanPerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:canPerformAction:forItemAtIndexPath:sender:)];
|
||||
_asyncDelegateFlags.collectionNodePerformActionForItem = [_asyncDelegate respondsToSelector:@selector(collectionNode:performAction:forItemAtIndexPath:sender:)];
|
||||
}
|
||||
|
||||
super.delegate = (id<UICollectionViewDelegate>)_proxyDelegate;
|
||||
|
||||
if (_layoutInspectorFlags.layoutInspectorDidChangeCollectionViewDelegate) {
|
||||
if (_layoutInspectorFlags.didChangeCollectionViewDelegate) {
|
||||
[self.layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
|
||||
}
|
||||
}
|
||||
@@ -453,9 +504,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
{
|
||||
_layoutInspector = layoutInspector;
|
||||
|
||||
_layoutInspectorFlags.layoutInspectorDidChangeCollectionViewDataSource = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDataSource:)];
|
||||
_layoutInspectorFlags.layoutInspectorDidChangeCollectionViewDelegate = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDelegate:)];
|
||||
_layoutInspectorFlags.layoutInspectorScrollableDirections = [_layoutInspector respondsToSelector:@selector(scrollableDirections)];
|
||||
_layoutInspectorFlags.didChangeCollectionViewDataSource = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDataSource:)];
|
||||
_layoutInspectorFlags.didChangeCollectionViewDelegate = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDelegate:)];
|
||||
_layoutInspectorFlags.scrollableDirections = [_layoutInspector respondsToSelector:@selector(scrollableDirections)];
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
|
||||
@@ -480,7 +531,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [[_dataController nodeAtIndexPath:indexPath] calculatedSize];
|
||||
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
|
||||
}
|
||||
|
||||
- (NSArray<NSArray <ASCellNode *> *> *)completedNodes
|
||||
@@ -490,7 +541,24 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [_dataController nodeAtIndexPath:indexPath];
|
||||
return [_dataController nodeAtCompletedIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait
|
||||
{
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
NSIndexPath *viewIndexPath = [self indexPathForNode:node];
|
||||
if (viewIndexPath == nil && wait) {
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
viewIndexPath = [self indexPathForNode:node];
|
||||
}
|
||||
return viewIndexPath;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)convertIndexPathToCollectionNode:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
|
||||
return [_dataController indexPathForNode:node];
|
||||
}
|
||||
|
||||
- (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
@@ -500,7 +568,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
return [_dataController indexPathForNode:cellNode];
|
||||
return [_dataController completedIndexPathForNode:cellNode];
|
||||
}
|
||||
|
||||
- (NSArray *)visibleNodes
|
||||
@@ -519,20 +587,38 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
return visibleNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This method was built when the distinction between data source
|
||||
* index paths and view index paths was unclear. For compatibility, it
|
||||
* still expects data source index paths for the time being.
|
||||
*/
|
||||
- (void)scrollToItemAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UICollectionViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[super scrollToItemAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
|
||||
NSIndexPath *viewIndexPath = [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES];
|
||||
if (viewIndexPath != nil) {
|
||||
[super scrollToItemAtIndexPath:viewIndexPath atScrollPosition:scrollPosition animated:animated];
|
||||
} else {
|
||||
NSLog(@"Warning: Ignoring request to scroll to item at index path %@ because the item did not reach the collection view.", indexPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This method was built when the distinction between data source
|
||||
* index paths and view index paths was unclear. For compatibility, it
|
||||
* still expects data source index paths for the time being.
|
||||
*/
|
||||
- (void)selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[super selectItemAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition];
|
||||
NSIndexPath *viewIndexPath = [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:YES];
|
||||
if (viewIndexPath != nil) {
|
||||
[super selectItemAtIndexPath:viewIndexPath animated:animated scrollPosition:scrollPosition];
|
||||
} else {
|
||||
NSLog(@"Warning: Ignoring request to select item at index path %@ because the item did not reach the collection view.", indexPath);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Internal
|
||||
@@ -653,17 +739,17 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
{
|
||||
_superIsPendingDataLoad = NO;
|
||||
return [_dataController numberOfSections];
|
||||
return [_dataController completedNumberOfSections];
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return [_dataController numberOfRowsInSection:section];
|
||||
return [_dataController completedNumberOfRowsInSection:section];
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [[_dataController nodeAtIndexPath:indexPath] calculatedSize];
|
||||
return [[self nodeForItemAtIndexPath:indexPath] calculatedSize];
|
||||
}
|
||||
|
||||
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
@@ -682,7 +768,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
{
|
||||
_ASCollectionViewCell *cell = [self dequeueReusableCellWithReuseIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
|
||||
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
ASCellNode *node = [self nodeForItemAtIndexPath:indexPath];
|
||||
cell.node = node;
|
||||
[_rangeController configureContentView:cell.contentView forCellNode:node];
|
||||
|
||||
@@ -711,11 +797,13 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath);
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPath) {
|
||||
[_asyncDelegate collectionView:self willDisplayNode:cellNode forItemAtIndexPath:indexPath];
|
||||
if (_asyncDelegateFlags.collectionNodeWillDisplayItem) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode willDisplayItemWithNode:cellNode];
|
||||
} else if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
} else if (_asyncDelegateFlags.asyncDelegateCollectionViewWillDisplayNodeForItemAtIndexPathDeprecated) {
|
||||
[_asyncDelegate collectionView:self willDisplayNode:cellNode forItemAtIndexPath:indexPath];
|
||||
} else if (_asyncDelegateFlags.collectionViewWillDisplayNodeForItemDeprecated) {
|
||||
[_asyncDelegate collectionView:self willDisplayNodeForItemAtIndexPath:indexPath];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
@@ -730,10 +818,15 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(_ASCollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASCellNode *cellNode = [cell node];
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateCollectionViewDidEndDisplayingNodeForItemAtIndexPath) {
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
if (_asyncDelegateFlags.collectionNodeDidEndDisplayingItem) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode didEndDisplayingItemWithNode:cellNode];
|
||||
} else if (_asyncDelegateFlags.collectionViewDidEndDisplayingNodeForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self didEndDisplayingNode:cellNode forItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
[_rangeController setNeedsUpdate];
|
||||
@@ -744,6 +837,180 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
cell.layoutAttributes = nil;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeWillDisplaySupplementaryElement) {
|
||||
ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath];
|
||||
ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind);
|
||||
[_asyncDelegate collectionNode:self.collectionNode willDisplaySupplementaryElementWithNode:node];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeDidEndDisplayingSupplementaryElement) {
|
||||
ASCellNode *node = [self supplementaryNodeForElementKind:elementKind atIndexPath:indexPath];
|
||||
ASDisplayNodeAssert([node.supplementaryElementKind isEqualToString:elementKind], @"Expected node for supplementary element to have kind '%@', got '%@'.", elementKind, node.supplementaryElementKind);
|
||||
[_asyncDelegate collectionNode:self.collectionNode didEndDisplayingSupplementaryElementWithNode:node];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeShouldSelectItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate collectionNode:self.collectionNode shouldSelectItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewShouldSelectItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate collectionView:self shouldSelectItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeDidSelectItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode didSelectItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewDidSelectItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self didSelectItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeShouldDeselectItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate collectionNode:self.collectionNode shouldDeselectItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewShouldDeselectItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate collectionView:self shouldDeselectItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeDidDeselectItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode didDeselectItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewDidDeselectItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self didDeselectItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeShouldHighlightItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate collectionNode:self.collectionNode shouldHighlightItemAtIndexPath:indexPath];
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewShouldHighlightItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate collectionView:self shouldHighlightItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeDidHighlightItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode didHighlightItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewDidHighlightItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self didHighlightItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeDidUnhighlightItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode didUnhighlightItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewDidUnhighlightItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self didUnhighlightItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeShouldShowMenuForItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate collectionNode:self.collectionNode shouldShowMenuForItemAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewShouldShowMenuForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate collectionView:self shouldShowMenuForItemAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(nonnull SEL)action forItemAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodeCanPerformActionForItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate collectionNode:self.collectionNode canPerformAction:action forItemAtIndexPath:indexPath sender:sender];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewCanPerformActionForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate collectionView:self canPerformAction:action forItemAtIndexPath:indexPath withSender:sender];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView performAction:(nonnull SEL)action forItemAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
|
||||
{
|
||||
if (_asyncDelegateFlags.collectionNodePerformActionForItem) {
|
||||
indexPath = [self convertIndexPathToCollectionNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate collectionNode:self.collectionNode performAction:action forItemAtIndexPath:indexPath sender:sender];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.collectionViewPerformActionForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self performAction:action forItemAtIndexPath:indexPath withSender:sender];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
@@ -759,7 +1026,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:collectionCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewDidScroll) {
|
||||
if (_asyncDelegateFlags.scrollViewDidScroll) {
|
||||
[_asyncDelegate scrollViewDidScroll:scrollView];
|
||||
}
|
||||
}
|
||||
@@ -777,7 +1044,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
|
||||
}
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset) {
|
||||
if (_asyncDelegateFlags.scrollViewWillEndDragging) {
|
||||
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:(targetContentOffset ? : &contentOffset)];
|
||||
}
|
||||
}
|
||||
@@ -789,7 +1056,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:collectionCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging) {
|
||||
if (_asyncDelegateFlags.scrollViewWillBeginDragging) {
|
||||
[_asyncDelegate scrollViewWillBeginDragging:scrollView];
|
||||
}
|
||||
}
|
||||
@@ -801,7 +1068,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:collectionCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging) {
|
||||
if (_asyncDelegateFlags.scrollViewDidEndDragging) {
|
||||
[_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
|
||||
}
|
||||
}
|
||||
@@ -846,7 +1113,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (ASScrollDirection)scrollableDirections
|
||||
{
|
||||
if (_layoutInspectorFlags.layoutInspectorScrollableDirections) {
|
||||
if (_layoutInspectorFlags.scrollableDirections) {
|
||||
return [self.layoutInspector scrollableDirections];
|
||||
} else {
|
||||
ASScrollDirection scrollableDirection = ASScrollDirectionNone;
|
||||
@@ -911,9 +1178,14 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
- (BOOL)canBatchFetch
|
||||
{
|
||||
// if the delegate does not respond to this method, there is no point in starting to fetch
|
||||
BOOL canFetch = _asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext;
|
||||
if (canFetch && _asyncDelegateFlags.asyncDelegateShouldBatchFetchForCollectionView) {
|
||||
BOOL canFetch = _asyncDelegateFlags.collectionNodeWillBeginBatchFetch || _asyncDelegateFlags.collectionViewWillBeginBatchFetch;
|
||||
if (canFetch && _asyncDelegateFlags.shouldBatchFetchForCollectionNode) {
|
||||
return [_asyncDelegate shouldBatchFetchForCollectionNode:self.collectionNode];
|
||||
} else if (canFetch && _asyncDelegateFlags.shouldBatchFetchForCollectionView) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate shouldBatchFetchForCollectionView:self];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return canFetch;
|
||||
}
|
||||
@@ -952,9 +1224,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
- (void)_beginBatchFetching
|
||||
{
|
||||
[_batchContext beginBatchFetching];
|
||||
if (_asyncDelegateFlags.asyncDelegateCollectionViewWillBeginBatchFetchWithContext) {
|
||||
if (_asyncDelegateFlags.collectionViewWillBeginBatchFetch) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate collectionView:self willBeginBatchFetchWithContext:_batchContext];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -962,28 +1237,48 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
#pragma mark - ASDataControllerSource
|
||||
|
||||
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (!_asyncDataSourceFlags.asyncDataSourceNodeBlockForItemAtIndexPath) {
|
||||
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath {
|
||||
ASCellNodeBlock block = nil;
|
||||
|
||||
if (_asyncDataSourceFlags.collectionNodeNodeBlockForItem) {
|
||||
block = [_asyncDataSource collectionNode:self.collectionNode nodeBlockForItemAtIndexPath:indexPath];
|
||||
} else if (_asyncDataSourceFlags.collectionNodeNodeForItem) {
|
||||
ASCellNode *node = [_asyncDataSource collectionNode:self.collectionNode nodeForItemAtIndexPath:indexPath];
|
||||
if ([node isKindOfClass:[ASCellNode class]]) {
|
||||
block = ^{
|
||||
return node;
|
||||
};
|
||||
} else {
|
||||
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableNode:nodeForRowAtIndexPath:. Node: %@", node);
|
||||
}
|
||||
} else if (_asyncDataSourceFlags.collectionViewNodeBlockForItem) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
block = [_asyncDataSource collectionView:self nodeBlockForItemAtIndexPath:indexPath];
|
||||
} else if (_asyncDataSourceFlags.collectionViewNodeForItem) {
|
||||
ASCellNode *node = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
|
||||
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
return ^{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
if (node.interactionDelegate == nil) {
|
||||
node.interactionDelegate = strongSelf;
|
||||
}
|
||||
return node;
|
||||
#pragma clang diagnostic pop
|
||||
if ([node isKindOfClass:[ASCellNode class]]) {
|
||||
block = ^{
|
||||
return node;
|
||||
};
|
||||
} else {
|
||||
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableView:nodeForRowAtIndexPath:. Node: %@", node);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle nil node block
|
||||
if (block == nil) {
|
||||
ASDisplayNodeFailAssert(@"ASTableNode could not get a node block for row at index path %@", indexPath);
|
||||
block = ^{
|
||||
return [[ASCellNode alloc] init];
|
||||
};
|
||||
}
|
||||
|
||||
ASCellNodeBlock block = [_asyncDataSource collectionView:self nodeBlockForItemAtIndexPath:indexPath];
|
||||
ASDisplayNodeAssertNotNil(block, @"Invalid block, expected nonnull ASCellNodeBlock");
|
||||
// Wrap the node block
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
return ^{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
|
||||
ASCellNode *node = (block != nil ? block() : [[ASCellNode alloc] init]);
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
if (node.interactionDelegate == nil) {
|
||||
@@ -991,6 +1286,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
}
|
||||
return node;
|
||||
};
|
||||
return block;
|
||||
}
|
||||
|
||||
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
@@ -1000,12 +1296,26 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
|
||||
{
|
||||
return [_asyncDataSource collectionView:self numberOfItemsInSection:section];
|
||||
if (_asyncDataSourceFlags.collectionNodeNumberOfItemsInSection) {
|
||||
return [_asyncDataSource collectionNode:self.collectionNode numberOfItemsInSection:section];
|
||||
} else if (_asyncDataSourceFlags.collectionViewNumberOfItemsInSection) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDataSource collectionView:self numberOfItemsInSection:section];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController {
|
||||
if (_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInCollectionView) {
|
||||
if (_asyncDataSourceFlags.numberOfSectionsInCollectionNode) {
|
||||
return [_asyncDataSource numberOfSectionsInCollectionNode:self.collectionNode];
|
||||
} else if (_asyncDataSourceFlags.numberOfSectionsInCollectionView) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDataSource numberOfSectionsInCollectionView:self];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
@@ -1013,21 +1323,27 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (id<ASEnvironment>)dataControllerEnvironment
|
||||
{
|
||||
if (self.collectionNode) {
|
||||
return self.collectionNode;
|
||||
}
|
||||
return self.strongCollectionNode;
|
||||
return self.collectionNode;
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionViewDataControllerSource
|
||||
|
||||
- (ASCellNode *)dataController:(ASCollectionDataController *)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");
|
||||
ASCellNode *node = nil;
|
||||
if (_asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement) {
|
||||
node = [_asyncDataSource collectionNode:self.collectionNode nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath];
|
||||
} else if (_asyncDataSourceFlags.collectionViewNodeForSupplementaryElement) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
node = [_asyncDataSource collectionView:self nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
ASDisplayNodeAssert(node != nil, @"A node must be returned for supplementary element of kind '%@' at index path '%@'", kind, indexPath);
|
||||
return node;
|
||||
}
|
||||
|
||||
// TODO: Lock this
|
||||
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController
|
||||
{
|
||||
return [_registeredSupplementaryKinds allObjects];
|
||||
@@ -1048,8 +1364,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
ASDisplayNodeAssertMainThread();
|
||||
id<ASSectionContext> context = nil;
|
||||
|
||||
if (_asyncDataSourceFlags.asyncDataSourceContextForSection) {
|
||||
context = [_asyncDataSource collectionView:self contextForSection:section];
|
||||
if (_asyncDataSourceFlags.collectionNodeContextForSection) {
|
||||
context = [_asyncDataSource collectionNode:self.collectionNode contextForSection:section];
|
||||
}
|
||||
|
||||
if (context != nil) {
|
||||
@@ -1092,7 +1408,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
|
||||
- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [_dataController nodeAtIndexPath:indexPath];
|
||||
return [self nodeForItemAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (NSString *)nameForRangeControllerDataSource
|
||||
@@ -1239,9 +1555,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
NSIndexPath *indexPath = [self indexPathForNode:node];
|
||||
if (indexPath) {
|
||||
if (node.isSelected) {
|
||||
[self selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||||
[super selectItemAtIndexPath:indexPath animated:NO scrollPosition:UICollectionViewScrollPositionNone];
|
||||
} else {
|
||||
[self deselectItemAtIndexPath:indexPath animated:NO];
|
||||
[super deselectItemAtIndexPath:indexPath animated:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1262,12 +1578,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
return;
|
||||
}
|
||||
|
||||
NSIndexPath *indexPath = [self indexPathForNode:node];
|
||||
if (indexPath == nil) {
|
||||
NSIndexPath *uikitIndexPath = [self indexPathForNode:node];
|
||||
if (uikitIndexPath == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:@[ indexPath ] batched:NO];
|
||||
[_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:@[ uikitIndexPath ] batched:NO];
|
||||
|
||||
ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle;
|
||||
if (invalidationStyle == ASCollectionViewInvalidationStyleNone) {
|
||||
@@ -1372,6 +1688,8 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height);
|
||||
|
||||
if (changedInNonScrollingDirection) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// This actually doesn't perform an animation, but prevents the transaction block from being processed in the
|
||||
// data controller's prevent animation block that would interrupt an interrupted relayout happening in an animation block
|
||||
// ie. ASCollectionView bounds change on rotation or multi-tasking split view resize.
|
||||
@@ -1382,6 +1700,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[self.collectionViewLayout invalidateLayout];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1402,16 +1721,6 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
|
||||
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this data source method.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this delegate method.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASDisplayNodeAssert(![self.asyncDataSource respondsToSelector:_cmd], @"%@ is not supported by ASCollectionView - please remove or disable this delegate method.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,19 +13,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a subset of UICollectionViewDataSource.
|
||||
*
|
||||
* @see ASCollectionViewDataSource
|
||||
* @see ASCollectionDataSource
|
||||
*/
|
||||
@protocol ASCommonCollectionViewDataSource <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section;
|
||||
@protocol ASCommonCollectionDataSource <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
@@ -33,29 +31,29 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a subset of UICollectionViewDelegate.
|
||||
*
|
||||
* @see ASCollectionViewDelegate
|
||||
* @see ASCollectionDelegate
|
||||
*/
|
||||
@protocol ASCommonCollectionViewDelegate <NSObject, UIScrollViewDelegate>
|
||||
@protocol ASCommonCollectionDelegate <NSObject, UIScrollViewDelegate>
|
||||
|
||||
@optional
|
||||
|
||||
- (UICollectionViewTransitionLayout *)collectionView:(UICollectionView *)collectionView transitionLayoutForOldLayout:(UICollectionViewLayout *)fromLayout newLayout:(UICollectionViewLayout *)toLayout;
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView willDisplaySupplementaryView:(UICollectionReusableView *)view forElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingSupplementaryView:(UICollectionReusableView *)view forElementOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASPagerFlowLayout.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
|
||||
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionViewDelegateFlowLayout, ASDelegateProxyInterceptor>
|
||||
{
|
||||
@@ -63,8 +64,8 @@
|
||||
[super didLoad];
|
||||
|
||||
ASCollectionView *cv = self.view;
|
||||
cv.asyncDataSource = (id<ASCollectionViewDataSource>)_proxyDataSource ?: self;
|
||||
cv.asyncDelegate = (id<ASCollectionViewDelegate>)_proxyDelegate ?: self;
|
||||
cv.asyncDataSource = (id<ASCollectionDataSource>)_proxyDataSource ?: self;
|
||||
cv.asyncDelegate = (id<ASCollectionDelegate>)_proxyDelegate ?: self;
|
||||
#if TARGET_OS_IOS
|
||||
cv.pagingEnabled = YES;
|
||||
cv.scrollsToTop = NO;
|
||||
@@ -109,7 +110,7 @@
|
||||
return [self.view nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]];
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionViewDataSource
|
||||
#pragma mark - ASCollectionDataSource
|
||||
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
|
||||
@@ -35,6 +35,476 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (weak, nonatomic) id <ASTableDelegate> delegate;
|
||||
@property (weak, nonatomic) id <ASTableDataSource> dataSource;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in full mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param rangeMode The range mode to get the running parameters for.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in the given mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeMode The range mode to set the running parameters for.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The data source must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param animated NO to disable animations for this batch
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The data source must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections, with an option to animate the insertion.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections, with an option to animate the deletion.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections using a given animation effect.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing a row index and section index that together identify a row.
|
||||
*
|
||||
* @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Deletes the rows specified by an array of index paths, with an option to animate the deletion.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the rows to delete.
|
||||
*
|
||||
* @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Reloads the specified rows using a given animation effect.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the rows to reload.
|
||||
*
|
||||
* @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Moves the row at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the row to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the row.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
|
||||
/**
|
||||
* Retrieves the number of rows in the given section.
|
||||
*
|
||||
* @param section The section.
|
||||
*
|
||||
* @return The number of rows.
|
||||
*/
|
||||
- (NSInteger)numberOfRowsInSection:(NSInteger)section AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* The number of sections in the table node.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSInteger numberOfSections;
|
||||
|
||||
/**
|
||||
* Retrieves the node for the row at the given index path.
|
||||
*/
|
||||
- (nullable ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a node for a row.
|
||||
*
|
||||
* @return The index path to this row, if it exists.
|
||||
*
|
||||
* @discussion This method will return @c nil for a node that is still being
|
||||
* displayed in the table view, if the data source has deleted the row.
|
||||
* That is, the node is visible but it no longer corresponds
|
||||
* to any item in the data source and will be removed soon.
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDataSource.
|
||||
*/
|
||||
@protocol ASTableDataSource <ASCommonTableDataSource, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Asks the data source for the number of sections in the table node.
|
||||
*
|
||||
* @see @c numberOfSectionsInTableView:
|
||||
*/
|
||||
- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode;
|
||||
|
||||
/**
|
||||
* Asks the data source for the number of rows in the given section of the table node.
|
||||
*
|
||||
* @see @c numberOfSectionsInTableView:
|
||||
*/
|
||||
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section;
|
||||
|
||||
/**
|
||||
* Asks the data source for a block to create a node to represent the row at the given index path.
|
||||
* The block will be run by the table node concurrently in the background before the row is inserted
|
||||
* into the table view.
|
||||
*
|
||||
* @param tableNode The sender.
|
||||
* @param indexPath The index path of the row.
|
||||
*
|
||||
* @return a block that creates the node for display at this indexpath.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
*
|
||||
* @note This method takes precedence over tableNode:nodeForRowAtIndexPath: if implemented.
|
||||
*/
|
||||
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Asks the data source for a node to represent the row at the given index path.
|
||||
*
|
||||
* @param tableNode The sender.
|
||||
* @param indexPath The index path of the row.
|
||||
*
|
||||
* @return a node to display for this row. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method
|
||||
* is not called when the row is about to display.
|
||||
*/
|
||||
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Similar to -tableView:cellForRowAtIndexPath:.
|
||||
*
|
||||
* @param tableNode The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method
|
||||
* is not called when the row is about to display.
|
||||
*/
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -tableView:nodeForRowAtIndexPath:
|
||||
* This method takes precedence over tableView:nodeForRowAtIndexPath: if implemented.
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a block that creates the node for display at this indexpath.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
*/
|
||||
- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data fetching in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDelegate.
|
||||
*
|
||||
* Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are
|
||||
* responsible for deciding their preferred onscreen height in -calculateSizeThatFits:.
|
||||
*/
|
||||
@protocol ASTableDelegate <ASCommonTableViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (void)tableNode:(ASTableNode *)tableNode willDisplayRowWithNode:(ASCellNode *)node;
|
||||
|
||||
- (void)tableNode:(ASTableNode *)tableNode didEndDisplayingRowWithNode:(ASCellNode *)node;
|
||||
|
||||
- (nullable NSIndexPath *)tableNode:(ASTableNode *)tableNode willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (nullable NSIndexPath *)tableNode:(ASTableNode *)tableNode willDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (void)tableNode:(ASTableNode *)tableNode didDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (BOOL)tableNode:(ASTableNode *)tableNode shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableNode:(ASTableNode *)tableNode didHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableNode:(ASTableNode *)tableNode didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (BOOL)tableNode:(ASTableNode *)tableNode shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)tableNode:(ASTableNode *)tableNode canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
- (void)tableNode:(ASTableNode *)tableNode performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the row at the index path.
|
||||
* Note: the widths in the returned size range are ignored!
|
||||
*
|
||||
* @param tableNode The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @return A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)tableNode:(ASTableNode *)tableNode constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param context A context object that must be notified when the batch fetch is completed.
|
||||
*
|
||||
* @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future
|
||||
* notifications to do batch fetches. This method is called on a background queue.
|
||||
*
|
||||
* ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a
|
||||
* UIRefreshControl.
|
||||
*/
|
||||
- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context;
|
||||
|
||||
/**
|
||||
* Tell the tableView if batch fetching should begin.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view will add the given node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param node The node that will be displayed.
|
||||
* @param indexPath The index path of the row that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view did remove the provided node from the view hierarchy.
|
||||
* This may be caused by the node scrolling out of view, or by deleting the row
|
||||
* or its containing section with @c deleteRowsAtIndexPaths:withRowAnimation: or @c deleteSections:withRowAnimation: .
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param node The node which was removed from the view hierarchy.
|
||||
* @param indexPath The index path at which the node was located before the removal.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param context A context object that must be notified when the batch fetch is completed.
|
||||
*
|
||||
* @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future
|
||||
* notifications to do batch fetches. This method is called on a background queue.
|
||||
*
|
||||
* ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a
|
||||
* UIRefreshControl.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Tell the tableView if batch fetching should begin.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the row at the index path.
|
||||
* Note: the widths in the returned size range are ignored!
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @return A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view will add the node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param indexPath The index path of the row that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*
|
||||
* This method is deprecated. Use @c tableView:willDisplayNode:forRowAtIndexPath: instead.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
#import "AsyncDisplayKit+Debug.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
|
||||
#pragma mark - _ASTablePendingState
|
||||
|
||||
@@ -70,7 +71,7 @@
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
|
||||
{
|
||||
ASDisplayNodeViewBlock tableViewBlock = ^UIView *{
|
||||
return [[ASTableView alloc] _initWithFrame:frame style:style dataControllerClass:dataControllerClass ownedByNode:YES];
|
||||
return [[ASTableView alloc] _initWithFrame:frame style:style dataControllerClass:dataControllerClass];
|
||||
};
|
||||
|
||||
if (self = [super initWithViewBlock:tableViewBlock]) {
|
||||
@@ -109,6 +110,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
self.delegate = nil;
|
||||
self.dataSource = nil;
|
||||
}
|
||||
|
||||
- (ASTableView *)view
|
||||
{
|
||||
return (ASTableView *)[super view];
|
||||
@@ -117,13 +124,13 @@
|
||||
- (void)clearContents
|
||||
{
|
||||
[super clearContents];
|
||||
[self.view clearContents];
|
||||
[self.rangeController clearContents];
|
||||
}
|
||||
|
||||
- (void)clearFetchedData
|
||||
{
|
||||
[super clearFetchedData];
|
||||
[self.view clearFetchedData];
|
||||
[self.rangeController clearFetchedData];
|
||||
}
|
||||
|
||||
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
|
||||
@@ -148,6 +155,18 @@
|
||||
|
||||
#pragma mark Setter / Getter
|
||||
|
||||
// TODO: Implement this without the view.
|
||||
- (ASDataController *)dataController
|
||||
{
|
||||
return self.view.dataController;
|
||||
}
|
||||
|
||||
// TODO: Implement this without the view.
|
||||
- (ASRangeController *)rangeController
|
||||
{
|
||||
return self.view.rangeController;
|
||||
}
|
||||
|
||||
- (_ASTablePendingState *)pendingState
|
||||
{
|
||||
if (!_pendingState && ![self isNodeLoaded]) {
|
||||
@@ -191,6 +210,7 @@
|
||||
if ([self pendingState]) {
|
||||
return _pendingState.dataSource;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
|
||||
return self.view.asyncDataSource;
|
||||
}
|
||||
}
|
||||
@@ -203,7 +223,7 @@
|
||||
_pendingState.rangeMode = rangeMode;
|
||||
} else {
|
||||
ASDisplayNodeAssert([self isNodeLoaded], @"ASTableNode should be loaded if pendingState doesn't exist");
|
||||
[self.view.rangeController updateCurrentRangeWithMode:rangeMode];
|
||||
[self.rangeController updateCurrentRangeWithMode:rangeMode];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,4 +231,117 @@
|
||||
|
||||
ASEnvironmentCollectionTableSetEnvironmentState(_environmentStateLock)
|
||||
|
||||
#pragma mark - Range Tuning
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.rangeController tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
[self.rangeController setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
|
||||
{
|
||||
return [self.rangeController setTuningParameters:tuningParameters forRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
#pragma mark - Querying Data
|
||||
|
||||
- (NSInteger)numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return [self.dataController numberOfRowsInSection:section];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSections
|
||||
{
|
||||
return [self.dataController numberOfSections];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
return [self.dataController indexPathForNode:cellNode];
|
||||
}
|
||||
|
||||
- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [self.dataController nodeAtIndexPath:indexPath];
|
||||
}
|
||||
|
||||
#pragma mark - Editing
|
||||
|
||||
- (void)reloadDataWithCompletion:(void (^)())completion
|
||||
{
|
||||
[self.view reloadDataWithCompletion:completion];
|
||||
}
|
||||
|
||||
- (void)reloadData
|
||||
{
|
||||
[self reloadDataWithCompletion:nil];
|
||||
}
|
||||
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self.view beginUpdates];
|
||||
updates();
|
||||
[self.view endUpdatesAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self performBatchAnimated:YES updates:updates completion:completion];
|
||||
}
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view insertSections:sections withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view deleteSections:sections withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view reloadSections:sections withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
|
||||
{
|
||||
[self.view moveSection:section toSection:newSection];
|
||||
}
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view insertRowsAtIndexPaths:indexPaths withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
[self.view reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation];
|
||||
}
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
|
||||
{
|
||||
[self.view moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
|
||||
}
|
||||
|
||||
- (void)waitUntilAllUpdatesAreCommitted
|
||||
{
|
||||
[self.view waitUntilAllUpdatesAreCommitted];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -19,6 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class ASCellNode;
|
||||
@protocol ASTableDataSource;
|
||||
@protocol ASTableDelegate;
|
||||
@class ASTableNode;
|
||||
|
||||
/**
|
||||
* Asynchronous UITableView with Intelligent Preloading capabilities.
|
||||
@@ -34,9 +35,37 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface ASTableView : UITableView
|
||||
|
||||
/// The corresponding table node, or nil if one does not exist.
|
||||
@property (nonatomic, weak, readonly) ASTableNode *tableNode;
|
||||
|
||||
@property (nonatomic, weak) id<ASTableDelegate> asyncDelegate;
|
||||
@property (nonatomic, weak) id<ASTableDataSource> asyncDataSource;
|
||||
|
||||
/**
|
||||
* Retrieves the node for the row at the given index path.
|
||||
*/
|
||||
- (nullable ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* YES to automatically adjust the contentOffset when cells are inserted or deleted "before"
|
||||
* visible cells, maintaining the users' visible scroll position. Currently this feature tracks insertions, moves and deletions of
|
||||
* cells, but section edits are ignored.
|
||||
*
|
||||
* default is NO.
|
||||
*/
|
||||
@property (nonatomic) BOOL automaticallyAdjustsContentOffset;
|
||||
|
||||
/**
|
||||
* The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.
|
||||
*
|
||||
* Defaults to two screenfuls.
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableView (Deprecated)
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
*
|
||||
@@ -45,7 +74,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in full mode.
|
||||
@@ -57,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
@@ -68,7 +97,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
@@ -81,7 +110,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
@@ -93,30 +122,39 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* Defaults to two screenfuls.
|
||||
* @return an array containing the cell nodes being displayed on screen.
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
|
||||
- (NSArray<ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a cellNode part of the table view
|
||||
*
|
||||
* @return an indexPath for this cellNode
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
-(void)reloadDataWithCompletion:(void (^ _Nullable)())completion;
|
||||
-(void)reloadDataWithCompletion:(void (^ _Nullable)())completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
- (void)reloadData;
|
||||
- (void)reloadData ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
@@ -124,25 +162,25 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @warning This method is substantially more expensive than UITableView's version and will block the main thread while
|
||||
* all the cells load.
|
||||
*/
|
||||
- (void)reloadDataImmediately;
|
||||
- (void)reloadDataImmediately ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Triggers a relayout of all nodes.
|
||||
*
|
||||
* @discussion This method invalidates and lays out every cell node in the table view.
|
||||
*/
|
||||
- (void)relayoutItems;
|
||||
- (void)relayoutItems ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Begins a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block.
|
||||
*
|
||||
*
|
||||
* @discussion You call this method to bracket a series of method calls that ends with endUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. It's important to remember that the ASTableView will be processing the updates asynchronously after this call is completed.
|
||||
*
|
||||
* @warning This method must be called from the main thread.
|
||||
*/
|
||||
- (void)beginUpdates;
|
||||
- (void)beginUpdates ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block.
|
||||
@@ -153,11 +191,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @warning This method is must be called from the main thread.
|
||||
*/
|
||||
- (void)endUpdates;
|
||||
- (void)endUpdates ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view.
|
||||
* You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations
|
||||
* You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. This method is must be called from the main thread. It's important to remember that the ASTableView will
|
||||
* be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until
|
||||
@@ -168,24 +206,24 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion;
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted;
|
||||
- (void)waitUntilAllUpdatesAreCommitted ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections, with an option to animate the insertion.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
*
|
||||
* @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections, with an option to animate the deletion.
|
||||
@@ -197,7 +235,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections using a given animation effect.
|
||||
@@ -209,7 +247,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
@@ -221,7 +259,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion.
|
||||
@@ -233,7 +271,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Deletes the rows specified by an array of index paths, with an option to animate the deletion.
|
||||
@@ -245,7 +283,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Reloads the specified rows using a given animation effect.
|
||||
@@ -257,7 +295,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Moves the row at a specified location to a destination location.
|
||||
@@ -269,213 +307,21 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -cellForRowAtIndexPath:.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath.
|
||||
*/
|
||||
- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
/// Deprecated in 2.0. You should not call this method.
|
||||
- (void)clearContents ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a cellNode part of the table view
|
||||
*
|
||||
* @return an indexPath for this cellNode
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the cell nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* YES to automatically adjust the contentOffset when cells are inserted or deleted "before"
|
||||
* visible cells, maintaining the users' visible scroll position. Currently this feature tracks insertions, moves and deletions of
|
||||
* cells, but section edits are ignored.
|
||||
*
|
||||
* default is NO.
|
||||
*/
|
||||
@property (nonatomic) BOOL automaticallyAdjustsContentOffset;
|
||||
|
||||
/**
|
||||
* Triggers all loaded ASCellNodes to destroy displayed contents (freeing a lot of memory).
|
||||
*
|
||||
* @discussion This method should only be called by ASTableNode. To be removed in a later release.
|
||||
*/
|
||||
- (void)clearContents;
|
||||
|
||||
/**
|
||||
* Triggers all loaded ASCellNodes to purge any data fetched from the network or disk (freeing memory).
|
||||
*
|
||||
* @discussion This method should only be called by ASTableNode. To be removed in a later release.
|
||||
*/
|
||||
- (void)clearFetchedData;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDataSource.
|
||||
*/
|
||||
@protocol ASTableDataSource <ASCommonTableViewDataSource, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Similar to -tableView:cellForRowAtIndexPath:.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a node for display at this indexpath. This will be called on the main thread and should not implement reuse (it will be called once per row). Unlike UITableView's version, this method
|
||||
* is not called when the row is about to display.
|
||||
*/
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
/**
|
||||
* Similar to -tableView:nodeForRowAtIndexPath:
|
||||
* This method takes precedence over tableView:nodeForRowAtIndexPath: if implemented.
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the requested node.
|
||||
*
|
||||
* @return a block that creates the node for display at this indexpath.
|
||||
* Must be thread-safe (can be called on the main thread or a background
|
||||
* queue) and should not implement reuse (it will be called once per row).
|
||||
*/
|
||||
|
||||
- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Indicator to lock the data source for data fetching in async mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)tableViewLockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* Indicator to unlock the data source for data fetching in asyn mode.
|
||||
* We should not update the data source until the data source has been unlocked. Otherwise, it will incur data inconsistency or exception
|
||||
* due to the data access in async mode.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @deprecated The data source is always accessed on the main thread, and this method will not be called.
|
||||
*/
|
||||
- (void)tableViewUnlockDataSource:(ASTableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||
/// Deprecated in 2.0. You should not call this method.
|
||||
- (void)clearFetchedData ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
ASDISPLAYNODE_DEPRECATED
|
||||
@protocol ASTableViewDataSource <ASTableDataSource>
|
||||
@end
|
||||
|
||||
/**
|
||||
* This is a node-based UITableViewDelegate.
|
||||
*
|
||||
* Note that -tableView:heightForRowAtIndexPath: has been removed; instead, your custom ASCellNode subclasses are
|
||||
* responsible for deciding their preferred onscreen height in -calculateSizeThatFits:.
|
||||
*/
|
||||
@protocol ASTableDelegate <ASCommonTableViewDelegate, NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view will add the given node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param node The node that will be displayed.
|
||||
* @param indexPath The index path of the row that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willDisplayNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view did remove the provided node from the view hierarchy.
|
||||
* This may be caused by the node scrolling out of view, or by deleting the row
|
||||
* or its containing section with @c deleteRowsAtIndexPaths:withRowAnimation: or @c deleteSections:withRowAnimation: .
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param node The node which was removed from the view hierarchy.
|
||||
* @param indexPath The index path at which the node was located before the removal.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param context A context object that must be notified when the batch fetch is completed.
|
||||
*
|
||||
* @discussion You must eventually call -completeBatchFetching: with an argument of YES in order to receive future
|
||||
* notifications to do batch fetches. This method is called on a background queue.
|
||||
*
|
||||
* ASTableView currently only supports batch events for tail loads. If you require a head load, consider implementing a
|
||||
* UIRefreshControl.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context;
|
||||
|
||||
/**
|
||||
* Tell the tableView if batch fetching should begin.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @discussion Use this method to conditionally fetch batches. Example use cases are: limiting the total number of
|
||||
* objects that can be fetched or no network connection.
|
||||
*
|
||||
* If not implemented, the tableView assumes that it should notify its asyncDelegate when batch fetching
|
||||
* should occur.
|
||||
*/
|
||||
- (BOOL)shouldBatchFetchForTableView:(ASTableView *)tableView AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Provides the constrained size range for measuring the row at the index path.
|
||||
* Note: the widths in the returned size range are ignored!
|
||||
*
|
||||
* @param tableView The sender.
|
||||
*
|
||||
* @param indexPath The index path of the node.
|
||||
*
|
||||
* @return A constrained size range for layout the node at this index path.
|
||||
*/
|
||||
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Informs the delegate that the table view will add the node
|
||||
* at the given index path to the view hierarchy.
|
||||
*
|
||||
* @param tableView The sender.
|
||||
* @param indexPath The index path of the row that will be displayed.
|
||||
*
|
||||
* @warning AsyncDisplayKit processes table view edits asynchronously. The index path
|
||||
* passed into this method may not correspond to the same item in your data source
|
||||
* if your data source has been updated since the last edit was processed.
|
||||
*
|
||||
* This method is deprecated. Use @c tableView:willDisplayNode:forRowAtIndexPath: instead.
|
||||
*/
|
||||
- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
ASDISPLAYNODE_DEPRECATED
|
||||
@protocol ASTableViewDelegate <ASTableDelegate>
|
||||
@end
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
|
||||
static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero};
|
||||
static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
@@ -130,36 +131,61 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSMutableSet *_cellsForVisibilityUpdates;
|
||||
|
||||
struct {
|
||||
unsigned int asyncDelegateScrollViewDidScroll:1;
|
||||
unsigned int asyncDelegateScrollViewWillBeginDragging:1;
|
||||
unsigned int asyncDelegateScrollViewDidEndDragging:1;
|
||||
unsigned int asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath:1;
|
||||
unsigned int asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated:1;
|
||||
unsigned int asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath:1;
|
||||
unsigned int asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset:1;
|
||||
unsigned int asyncDelegateTableViewWillBeginBatchFetchWithContext:1;
|
||||
unsigned int asyncDelegateShouldBatchFetchForTableView:1;
|
||||
unsigned int asyncDelegateTableViewConstrainedSizeForRowAtIndexPath:1;
|
||||
unsigned int scrollViewDidScroll:1;
|
||||
unsigned int scrollViewWillBeginDragging:1;
|
||||
unsigned int scrollViewDidEndDragging:1;
|
||||
unsigned int scrollViewWillEndDragging:1;
|
||||
unsigned int tableNodeWillDisplayNodeForRow:1;
|
||||
unsigned int tableViewWillDisplayNodeForRow:1;
|
||||
unsigned int tableViewWillDisplayNodeForRowDeprecated:1;
|
||||
unsigned int tableNodeDidEndDisplayingNodeForRow:1;
|
||||
unsigned int tableViewDidEndDisplayingNodeForRow:1;
|
||||
unsigned int tableNodeWillBeginBatchFetch:1;
|
||||
unsigned int tableViewWillBeginBatchFetch:1;
|
||||
unsigned int shouldBatchFetchForTableView:1;
|
||||
unsigned int shouldBatchFetchForTableNode:1;
|
||||
unsigned int tableViewConstrainedSizeForRow:1;
|
||||
unsigned int tableNodeConstrainedSizeForRow:1;
|
||||
unsigned int tableViewWillSelectRow:1;
|
||||
unsigned int tableNodeWillSelectRow:1;
|
||||
unsigned int tableViewDidSelectRow:1;
|
||||
unsigned int tableNodeDidSelectRow:1;
|
||||
unsigned int tableViewWillDeselectRow:1;
|
||||
unsigned int tableNodeWillDeselectRow:1;
|
||||
unsigned int tableViewDidDeselectRow:1;
|
||||
unsigned int tableNodeDidDeselectRow:1;
|
||||
unsigned int tableViewShouldHighlightRow:1;
|
||||
unsigned int tableNodeShouldHighlightRow:1;
|
||||
unsigned int tableViewDidHighlightRow:1;
|
||||
unsigned int tableNodeDidHighlightRow:1;
|
||||
unsigned int tableViewDidUnhighlightRow:1;
|
||||
unsigned int tableNodeDidUnhighlightRow:1;
|
||||
unsigned int tableViewShouldShowMenuForRow:1;
|
||||
unsigned int tableNodeShouldShowMenuForRow:1;
|
||||
unsigned int tableViewCanPerformActionForRow:1;
|
||||
unsigned int tableNodeCanPerformActionForRow:1;
|
||||
unsigned int tableViewPerformActionForRow:1;
|
||||
unsigned int tableNodePerformActionForRow:1;
|
||||
} _asyncDelegateFlags;
|
||||
|
||||
struct {
|
||||
unsigned int asyncDataSourceNumberOfSectionsInTableView:1;
|
||||
unsigned int asyncDataSourceTableViewNodeBlockForRowAtIndexPath:1;
|
||||
unsigned int asyncDataSourceTableViewNodeForRowAtIndexPath:1;
|
||||
unsigned int asyncDataSourceTableViewCanMoveRowAtIndexPath:1;
|
||||
unsigned int asyncDataSourceTableViewMoveRowAtIndexPath:1;
|
||||
unsigned int numberOfSectionsInTableView:1;
|
||||
unsigned int numberOfSectionsInTableNode:1;
|
||||
unsigned int tableNodeNumberOfRowsInSection:1;
|
||||
unsigned int tableViewNumberOfRowsInSection:1;
|
||||
unsigned int tableViewNodeBlockForRow:1;
|
||||
unsigned int tableNodeNodeBlockForRow:1;
|
||||
unsigned int tableViewNodeForRow:1;
|
||||
unsigned int tableNodeNodeForRow:1;
|
||||
unsigned int tableViewCanMoveRow:1;
|
||||
unsigned int tableNodeCanMoveRow:1;
|
||||
unsigned int tableViewMoveRow:1;
|
||||
unsigned int tableNodeMoveRow:1;
|
||||
} _asyncDataSourceFlags;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong, readwrite) ASDataController *dataController;
|
||||
|
||||
// Used only when ASTableView is created directly rather than through ASTableNode.
|
||||
// We create a node so that logic related to appearance, memory management, etc can be located there
|
||||
// for both the node-based and view-based version of the table.
|
||||
// This also permits sharing logic with ASCollectionNode, as the superclass is not UIKit-controlled.
|
||||
@property (nonatomic, strong) ASTableNode *strongTableNode;
|
||||
|
||||
// Always set, whether ASCollectionView is created directly or via ASCollectionNode.
|
||||
@property (nonatomic, weak) ASTableNode *tableNode;
|
||||
|
||||
@property (nonatomic) BOOL test_enableSuperUpdateCallLogging;
|
||||
@@ -217,10 +243,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style
|
||||
{
|
||||
return [self _initWithFrame:frame style:style dataControllerClass:nil ownedByNode:NO];
|
||||
return [self _initWithFrame:frame style:style dataControllerClass:nil];
|
||||
}
|
||||
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass ownedByNode:(BOOL)ownedByNode
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass
|
||||
{
|
||||
if (!(self = [super initWithFrame:frame style:style])) {
|
||||
return nil;
|
||||
@@ -232,15 +258,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
[self configureWithDataControllerClass:dataControllerClass];
|
||||
|
||||
if (!ownedByNode) {
|
||||
// See commentary at the definition of .strongTableNode for why we create an ASTableNode.
|
||||
// FIXME: The _view pointer of the node retains us, but the node will die immediately if we don't
|
||||
// retain it. At the moment there isn't a great solution to this, so we can't yet move our core
|
||||
// logic to ASTableNode (required to have a shared superclass with ASCollection*).
|
||||
ASTableNode *tableNode = nil; //[[ASTableNode alloc] _initWithTableView:self];
|
||||
self.strongTableNode = tableNode;
|
||||
}
|
||||
|
||||
if (!AS_AT_LEAST_IOS9) {
|
||||
_retainedLayer = self.layer;
|
||||
}
|
||||
@@ -277,7 +294,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
ASDisplayNodeAssert(delegate == nil, @"ASTableView uses asyncDelegate, not UITableView's delegate property.");
|
||||
}
|
||||
|
||||
- (void)setAsyncDataSource:(id<ASTableViewDataSource>)asyncDataSource
|
||||
- (void)setAsyncDataSource:(id<ASTableDataSource>)asyncDataSource
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource
|
||||
@@ -294,20 +311,28 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
_asyncDataSource = asyncDataSource;
|
||||
_proxyDataSource = [[ASTableViewProxy alloc] initWithTarget:_asyncDataSource interceptor:self];
|
||||
|
||||
_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceTableViewCanMoveRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.asyncDataSourceTableViewMoveRowAtIndexPath = [_asyncDataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)];
|
||||
_asyncDataSourceFlags.numberOfSectionsInTableView = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableView:)];
|
||||
_asyncDataSourceFlags.numberOfSectionsInTableNode = [_asyncDataSource respondsToSelector:@selector(numberOfSectionsInTableNode:)];
|
||||
_asyncDataSourceFlags.tableViewNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableView:numberOfRowsInSection:)];
|
||||
_asyncDataSourceFlags.tableNodeNumberOfRowsInSection = [_asyncDataSource respondsToSelector:@selector(tableNode:numberOfRowsInSection:)];
|
||||
_asyncDataSourceFlags.tableViewNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.tableNodeNodeForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.tableViewNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.tableNodeNodeBlockForRow = [_asyncDataSource respondsToSelector:@selector(tableNode:nodeBlockForRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.tableViewCanMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:canMoveRowAtIndexPath:)];
|
||||
_asyncDataSourceFlags.tableViewMoveRow = [_asyncDataSource respondsToSelector:@selector(tableView:moveRowAtIndexPath:toIndexPath:)];
|
||||
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.asyncDataSourceTableViewNodeBlockForRowAtIndexPath
|
||||
|| _asyncDataSourceFlags.asyncDataSourceTableViewNodeForRowAtIndexPath, @"Data source must implement tableView:nodeBlockForRowAtIndexPath: or tableView:nodeForRowAtIndexPath:");
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.tableViewNodeBlockForRow
|
||||
|| _asyncDataSourceFlags.tableViewNodeForRow
|
||||
|| _asyncDataSourceFlags.tableNodeNodeBlockForRow
|
||||
|| _asyncDataSourceFlags.tableNodeNodeForRow, @"Data source must implement tableNode:nodeBlockForRowAtIndexPath: or tableNode:nodeForRowAtIndexPath:");
|
||||
ASDisplayNodeAssert(_asyncDataSourceFlags.tableNodeNumberOfRowsInSection || _asyncDataSourceFlags.tableViewNumberOfRowsInSection, @"Data source must implement tableNode:numberOfRowsInSection:");
|
||||
}
|
||||
|
||||
super.dataSource = (id<UITableViewDataSource>)_proxyDataSource;
|
||||
}
|
||||
|
||||
- (void)setAsyncDelegate:(id<ASTableViewDelegate>)asyncDelegate
|
||||
- (void)setAsyncDelegate:(id<ASTableDelegate>)asyncDelegate
|
||||
{
|
||||
// Note: It's common to check if the value hasn't changed and short-circuit but we aren't doing that here to handle
|
||||
// the (common) case of nilling the asyncDelegate in the ViewController's dealloc. In this case our _asyncDelegate
|
||||
@@ -324,19 +349,43 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
_asyncDelegate = asyncDelegate;
|
||||
_proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:_asyncDelegate interceptor:self];
|
||||
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
|
||||
_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)];
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath == NO) {
|
||||
_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)];
|
||||
}
|
||||
_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
|
||||
_asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
|
||||
_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
|
||||
_asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.scrollViewDidScroll = [_asyncDelegate respondsToSelector:@selector(scrollViewDidScroll:)];
|
||||
|
||||
_asyncDelegateFlags.tableViewWillDisplayNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNode:forRowAtIndexPath:)];
|
||||
if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow == NO) {
|
||||
_asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated = [_asyncDelegate respondsToSelector:@selector(tableView:willDisplayNodeForRowAtIndexPath:)];
|
||||
}
|
||||
_asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:didEndDisplayingNode:forRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.scrollViewWillEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
|
||||
_asyncDelegateFlags.tableViewWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableView:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.tableNodeWillBeginBatchFetch = [_asyncDelegate respondsToSelector:@selector(tableNode:willBeginBatchFetchWithContext:)];
|
||||
_asyncDelegateFlags.shouldBatchFetchForTableView = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableView:)];
|
||||
_asyncDelegateFlags.shouldBatchFetchForTableNode = [_asyncDelegate respondsToSelector:@selector(shouldBatchFetchForTableNode:)];
|
||||
_asyncDelegateFlags.scrollViewWillBeginDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
|
||||
_asyncDelegateFlags.scrollViewDidEndDragging = [_asyncDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
|
||||
_asyncDelegateFlags.tableViewConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableView:constrainedSizeForRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeConstrainedSizeForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:constrainedSizeForRowAtIndexPath:)];
|
||||
|
||||
_asyncDelegateFlags.tableViewWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willSelectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeWillSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willSelectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeDidSelectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didSelectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeWillDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:willDeselectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeDidDeselectRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didDeselectRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldHighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeShouldHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldHighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didHighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeDidHighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didHighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableView:didUnhighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeDidUnhighlightRow = [_asyncDelegate respondsToSelector:@selector(tableNode:didUnhighlightRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableView:shouldShowMenuForRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableNodeShouldShowMenuForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:shouldShowMenuForRowAtIndexPath:)];
|
||||
_asyncDelegateFlags.tableViewCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)];
|
||||
_asyncDelegateFlags.tableNodeCanPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:canPerformAction:forRowAtIndexPath:withSender:)];
|
||||
_asyncDelegateFlags.tableViewPerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableView:performAction:forRowAtIndexPath:withSender:)];
|
||||
_asyncDelegateFlags.tableNodePerformActionForRow = [_asyncDelegate respondsToSelector:@selector(tableNode:performAction:forRowAtIndexPath:withSender:)];
|
||||
}
|
||||
|
||||
super.delegate = (id<UITableViewDelegate>)_proxyDelegate;
|
||||
@@ -396,6 +445,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType];
|
||||
}
|
||||
|
||||
- (ASTableNode *)tableNode
|
||||
{
|
||||
return (ASTableNode *)ASViewToDisplayNode(self);
|
||||
}
|
||||
|
||||
- (NSArray<NSArray <ASCellNode *> *> *)completedNodes
|
||||
{
|
||||
return [_dataController completedNodes];
|
||||
@@ -403,12 +457,34 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [_dataController nodeAtIndexPath:indexPath];
|
||||
return [_dataController nodeAtCompletedIndexPath:indexPath];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait
|
||||
{
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
return [self indexPathForNode:node waitingIfNeeded:wait];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)convertIndexPathToTableNode:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASCellNode *node = [self nodeForRowAtIndexPath:indexPath];
|
||||
return [_dataController indexPathForNode:node];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
return [_dataController indexPathForNode:cellNode];
|
||||
return [self indexPathForNode:cellNode waitingIfNeeded:NO];
|
||||
}
|
||||
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode waitingIfNeeded:(BOOL)wait
|
||||
{
|
||||
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
if (indexPath == nil && wait) {
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (NSArray<ASCellNode *> *)visibleNodes
|
||||
@@ -450,28 +526,40 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This method was built when the distinction between data source
|
||||
* index paths and view index paths was unclear. For compatibility, it
|
||||
* still expects data source index paths for the time being.
|
||||
* When the behavior is changed (to use the view index path directly)
|
||||
* we should also remove the @c convertIndexPathFromTableNode: method.
|
||||
*/
|
||||
- (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
|
||||
}
|
||||
|
||||
- (void)scrollToNearestSelectedRowAtScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[super scrollToNearestSelectedRowAtScrollPosition:scrollPosition animated:animated];
|
||||
indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES];
|
||||
if (indexPath != nil) {
|
||||
[super scrollToRowAtIndexPath:indexPath atScrollPosition:scrollPosition animated:animated];
|
||||
} else {
|
||||
NSLog(@"Warning: Ignoring request to scroll to row at index path %@ because the item did not reach the table view.", indexPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: This method was built when the distinction between data source
|
||||
* index paths and view index paths was unclear. For compatibility, it
|
||||
* still expects data source index paths for the time being.
|
||||
*/
|
||||
- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
[super selectRowAtIndexPath:indexPath animated:animated scrollPosition:scrollPosition];
|
||||
indexPath = [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:YES];
|
||||
if (indexPath != nil) {
|
||||
[super selectRowAtIndexPath:indexPath animated:YES scrollPosition:scrollPosition];
|
||||
} else {
|
||||
NSLog(@"Warning: Ignoring request to select row at index path %@ because the item did not reach the table view.", indexPath);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
@@ -627,7 +715,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
_ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath];
|
||||
cell.delegate = self;
|
||||
|
||||
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
|
||||
ASCellNode *node = [_dataController nodeAtCompletedIndexPath:indexPath];
|
||||
if (node) {
|
||||
[_rangeController configureContentView:cell.contentView forCellNode:node];
|
||||
|
||||
@@ -652,17 +740,17 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return [_dataController numberOfSections];
|
||||
return [_dataController completedNumberOfSections];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return [_dataController numberOfRowsInSection:section];
|
||||
return [_dataController completedNumberOfRowsInSection:section];
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDataSourceFlags.asyncDataSourceTableViewCanMoveRowAtIndexPath) {
|
||||
if (_asyncDataSourceFlags.tableViewCanMoveRow) {
|
||||
return [_asyncDataSource tableView:self canMoveRowAtIndexPath:indexPath];
|
||||
} else {
|
||||
return NO;
|
||||
@@ -671,7 +759,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
|
||||
{
|
||||
if (_asyncDataSourceFlags.asyncDataSourceTableViewMoveRowAtIndexPath) {
|
||||
if (_asyncDataSourceFlags.tableViewMoveRow) {
|
||||
[_asyncDataSource tableView:self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath];
|
||||
}
|
||||
// Move node after informing data source in case they call nodeAtIndexPath:
|
||||
@@ -687,11 +775,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with cell that will be displayed not to be nil. indexPath: %@", indexPath);
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPath) {
|
||||
[_asyncDelegate tableView:self willDisplayNode:cellNode forRowAtIndexPath:indexPath];
|
||||
if (_asyncDelegateFlags.tableNodeWillDisplayNodeForRow) {
|
||||
[_asyncDelegate tableNode:self.tableNode willDisplayRowWithNode:cellNode];
|
||||
} else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
} else if (_asyncDelegateFlags.asyncDelegateTableViewWillDisplayNodeForRowAtIndexPathDeprecated) {
|
||||
[_asyncDelegate tableView:self willDisplayNode:cellNode forRowAtIndexPath:indexPath];
|
||||
} else if (_asyncDelegateFlags.tableViewWillDisplayNodeForRowDeprecated) {
|
||||
[_asyncDelegate tableView:self willDisplayNodeForRowAtIndexPath:indexPath];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
@@ -713,9 +803,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
[_rangeController setNeedsUpdate];
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewDidEndDisplayingNodeForRowAtIndexPath) {
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
ASDisplayNodeAssertNotNil(cellNode, @"Expected node associated with removed cell not to be nil.");
|
||||
if (_asyncDelegateFlags.tableNodeDidEndDisplayingNodeForRow) {
|
||||
[_asyncDelegate tableNode:self.tableNode didEndDisplayingRowWithNode:cellNode];
|
||||
} else if (_asyncDelegateFlags.tableViewDidEndDisplayingNodeForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self didEndDisplayingNode:cellNode forRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
[_cellsForVisibilityUpdates removeObject:cell];
|
||||
@@ -723,6 +818,171 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
cellNode.scrollView = nil;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeWillSelectRow) {
|
||||
NSIndexPath *result = [self convertIndexPathToTableNode:indexPath];
|
||||
// If this item was is gone, just let the table view do its default behavior and select.
|
||||
if (result == nil) {
|
||||
return indexPath;
|
||||
} else {
|
||||
result = [_asyncDelegate tableNode:self.tableNode willSelectRowAtIndexPath:result];
|
||||
result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES];
|
||||
return result;
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewWillSelectRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate tableView:self willSelectRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return indexPath;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeDidSelectRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate tableNode:self.tableNode didSelectRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewDidSelectRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self didSelectRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeWillDeselectRow) {
|
||||
NSIndexPath *result = [self convertIndexPathToTableNode:indexPath];
|
||||
// If this item was is gone, just let the table view do its default behavior and deselect.
|
||||
if (result == nil) {
|
||||
return indexPath;
|
||||
} else {
|
||||
result = [_asyncDelegate tableNode:self.tableNode willDeselectRowAtIndexPath:result];
|
||||
result = [self convertIndexPathFromTableNode:result waitingIfNeeded:YES];
|
||||
return result;
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewWillDeselectRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate tableView:self willDeselectRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeDidDeselectRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate tableNode:self.tableNode didDeselectRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewDidDeselectRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self didDeselectRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeShouldHighlightRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate tableNode:self.tableNode shouldHighlightRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewShouldHighlightRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate tableView:self shouldHighlightRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeDidHighlightRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate tableNode:self.tableNode didHighlightRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewDidHighlightRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self didHighlightRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeDidHighlightRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate tableNode:self.tableNode didUnhighlightRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewDidUnhighlightRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self didUnhighlightRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(nonnull NSIndexPath *)indexPath
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeShouldShowMenuForRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate tableNode:self.tableNode shouldShowMenuForRowAtIndexPath:indexPath];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewShouldShowMenuForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate tableView:self shouldShowMenuForRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodeCanPerformActionForRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
return [_asyncDelegate tableNode:self.tableNode canPerformAction:action forRowAtIndexPath:indexPath withSender:sender];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewCanPerformActionForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate tableView:self canPerformAction:action forRowAtIndexPath:indexPath withSender:sender];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView performAction:(nonnull SEL)action forRowAtIndexPath:(nonnull NSIndexPath *)indexPath withSender:(nullable id)sender
|
||||
{
|
||||
if (_asyncDelegateFlags.tableNodePerformActionForRow) {
|
||||
indexPath = [self convertIndexPathToTableNode:indexPath];
|
||||
if (indexPath != nil) {
|
||||
[_asyncDelegate tableNode:self.tableNode performAction:action forRowAtIndexPath:indexPath withSender:sender];
|
||||
}
|
||||
} else if (_asyncDelegateFlags.tableViewPerformActionForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self performAction:action forRowAtIndexPath:indexPath withSender:sender];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
@@ -737,7 +997,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:tableCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewDidScroll) {
|
||||
if (_asyncDelegateFlags.scrollViewDidScroll) {
|
||||
[_asyncDelegate scrollViewDidScroll:scrollView];
|
||||
}
|
||||
}
|
||||
@@ -755,7 +1015,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
[self _beginBatchFetchingIfNeededWithScrollView:self forScrollDirection:[self scrollDirection] contentOffset:*targetContentOffset];
|
||||
}
|
||||
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewWillEndDraggingWithVelocityTargetContentOffset) {
|
||||
if (_asyncDelegateFlags.scrollViewWillEndDragging) {
|
||||
[_asyncDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:(targetContentOffset ? : &contentOffset)];
|
||||
}
|
||||
}
|
||||
@@ -767,7 +1027,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:tableViewCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewWillBeginDragging) {
|
||||
if (_asyncDelegateFlags.scrollViewWillBeginDragging) {
|
||||
[_asyncDelegate scrollViewWillBeginDragging:scrollView];
|
||||
}
|
||||
}
|
||||
@@ -779,7 +1039,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
inScrollView:scrollView
|
||||
withCellFrame:tableViewCell.frame];
|
||||
}
|
||||
if (_asyncDelegateFlags.asyncDelegateScrollViewDidEndDragging) {
|
||||
if (_asyncDelegateFlags.scrollViewDidEndDragging) {
|
||||
[_asyncDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
|
||||
}
|
||||
}
|
||||
@@ -841,9 +1101,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
- (BOOL)canBatchFetch
|
||||
{
|
||||
// if the delegate does not respond to this method, there is no point in starting to fetch
|
||||
BOOL canFetch = _asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext;
|
||||
if (canFetch && _asyncDelegateFlags.asyncDelegateShouldBatchFetchForTableView) {
|
||||
BOOL canFetch = _asyncDelegateFlags.tableNodeWillBeginBatchFetch || _asyncDelegateFlags.tableViewWillBeginBatchFetch;
|
||||
if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableNode) {
|
||||
return [_asyncDelegate shouldBatchFetchForTableNode:self.tableNode];
|
||||
} else if (canFetch && _asyncDelegateFlags.shouldBatchFetchForTableView) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDelegate shouldBatchFetchForTableView:self];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return canFetch;
|
||||
}
|
||||
@@ -882,9 +1147,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
- (void)_beginBatchFetching
|
||||
{
|
||||
[_batchContext beginBatchFetching];
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewWillBeginBatchFetchWithContext) {
|
||||
if (_asyncDelegateFlags.tableNodeWillBeginBatchFetch) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[_asyncDelegate tableNode:self.tableNode willBeginBatchFetchWithContext:_batchContext];
|
||||
});
|
||||
} else if (_asyncDelegateFlags.tableViewWillBeginBatchFetch) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
[_asyncDelegate tableView:self willBeginBatchFetchWithContext:_batchContext];
|
||||
#pragma clang diagnostic pop
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1110,24 +1382,46 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
#pragma mark - ASDataControllerDelegate
|
||||
|
||||
- (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if (![_asyncDataSource respondsToSelector:@selector(tableView:nodeBlockForRowAtIndexPath:)]) {
|
||||
ASCellNodeBlock block = nil;
|
||||
|
||||
if (_asyncDataSourceFlags.tableNodeNodeBlockForRow) {
|
||||
block = [_asyncDataSource tableNode:self.tableNode nodeBlockForRowAtIndexPath:indexPath];
|
||||
} else if (_asyncDataSourceFlags.tableNodeNodeForRow) {
|
||||
ASCellNode *node = [_asyncDataSource tableNode:self.tableNode nodeForRowAtIndexPath:indexPath];
|
||||
if ([node isKindOfClass:[ASCellNode class]]) {
|
||||
block = ^{
|
||||
return node;
|
||||
};
|
||||
} else {
|
||||
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableNode:nodeForRowAtIndexPath:. Node: %@", node);
|
||||
}
|
||||
} else if (_asyncDataSourceFlags.tableViewNodeBlockForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath];
|
||||
} else if (_asyncDataSourceFlags.tableViewNodeForRow) {
|
||||
ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath];
|
||||
ASDisplayNodeAssert([node isKindOfClass:ASCellNode.class], @"invalid node class, expected ASCellNode");
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
return ^{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
if (node.interactionDelegate == nil) {
|
||||
node.interactionDelegate = strongSelf;
|
||||
}
|
||||
return node;
|
||||
#pragma clang diagnostic pop
|
||||
if ([node isKindOfClass:[ASCellNode class]]) {
|
||||
block = ^{
|
||||
return node;
|
||||
};
|
||||
} else {
|
||||
ASDisplayNodeFailAssert(@"Data source returned invalid node from tableView:nodeForRowAtIndexPath:. Node: %@", node);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle nil node block
|
||||
if (block == nil) {
|
||||
ASDisplayNodeFailAssert(@"ASTableNode could not get a node block for row at index path %@", indexPath);
|
||||
block = ^{
|
||||
return [[ASCellNode alloc] init];
|
||||
};
|
||||
}
|
||||
|
||||
ASCellNodeBlock block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath];
|
||||
ASDisplayNodeAssertNotNil(block, @"Invalid block, expected nonnull ASCellNodeBlock");
|
||||
// Wrap the node block
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
ASCellNodeBlock configuredNodeBlock = ^{
|
||||
return ^{
|
||||
__typeof__(self) strongSelf = weakSelf;
|
||||
ASCellNode *node = (block != nil ? block() : [[ASCellNode alloc] init]);
|
||||
[node enterHierarchyState:ASHierarchyStateRangeManaged];
|
||||
@@ -1136,14 +1430,22 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
}
|
||||
return node;
|
||||
};
|
||||
return configuredNodeBlock;
|
||||
return block;
|
||||
}
|
||||
|
||||
- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASSizeRange constrainedSize = kInvalidSizeRange;
|
||||
if (_asyncDelegateFlags.asyncDelegateTableViewConstrainedSizeForRowAtIndexPath) {
|
||||
if (_asyncDelegateFlags.tableNodeConstrainedSizeForRow) {
|
||||
ASSizeRange delegateConstrainedSize = [_asyncDelegate tableNode:self.tableNode constrainedSizeForRowAtIndexPath:indexPath];
|
||||
// ignore widths in the returned size range (for TableView)
|
||||
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height),
|
||||
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
|
||||
} else if (_asyncDelegateFlags.tableViewConstrainedSizeForRow) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
ASSizeRange delegateConstrainedSize = [_asyncDelegate tableView:self constrainedSizeForRowAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
// ignore widths in the returned size range (for TableView)
|
||||
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.min.height),
|
||||
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
|
||||
@@ -1156,13 +1458,27 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
|
||||
{
|
||||
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
||||
if (_asyncDataSourceFlags.tableNodeNumberOfRowsInSection) {
|
||||
return [_asyncDataSource tableNode:self.tableNode numberOfRowsInSection:section];
|
||||
} else if (_asyncDataSourceFlags.tableViewNumberOfRowsInSection) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDataSource tableView:self numberOfRowsInSection:section];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController
|
||||
{
|
||||
if (_asyncDataSourceFlags.asyncDataSourceNumberOfSectionsInTableView) {
|
||||
if (_asyncDataSourceFlags.numberOfSectionsInTableNode) {
|
||||
return [_asyncDataSource numberOfSectionsInTableNode:self.tableNode];
|
||||
} else if (_asyncDataSourceFlags.numberOfSectionsInTableView) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [_asyncDataSource numberOfSectionsInTableView:self];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
return 1; // default section number
|
||||
}
|
||||
@@ -1172,10 +1488,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (id<ASEnvironment>)dataControllerEnvironment
|
||||
{
|
||||
if (self.tableNode) {
|
||||
return self.tableNode;
|
||||
}
|
||||
return self.strongTableNode;
|
||||
return self.tableNode;
|
||||
}
|
||||
|
||||
#pragma mark - _ASTableViewCellDelegate
|
||||
@@ -1215,7 +1528,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (void)nodeSelectedStateDidChange:(ASCellNode *)node
|
||||
{
|
||||
NSIndexPath *indexPath = [self indexPathForNode:node];
|
||||
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node];
|
||||
if (indexPath) {
|
||||
if (node.isSelected) {
|
||||
[self selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
|
||||
@@ -1227,7 +1540,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
- (void)nodeHighlightedStateDidChange:(ASCellNode *)node
|
||||
{
|
||||
NSIndexPath *indexPath = [self indexPathForNode:node];
|
||||
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node];
|
||||
if (indexPath) {
|
||||
[self cellForRowAtIndexPath:indexPath].highlighted = node.isHighlighted;
|
||||
}
|
||||
@@ -1271,6 +1584,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
#pragma mark - Helper Methods
|
||||
|
||||
/// @note This should be a UIKit index path.
|
||||
- (BOOL)isIndexPath:(NSIndexPath *)indexPath immediateSuccessorOfIndexPath:(NSIndexPath *)anchor
|
||||
{
|
||||
if (!anchor || !indexPath) {
|
||||
@@ -1280,12 +1594,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
return (indexPath.row == anchor.row+1); // assumes that indexes are valid
|
||||
|
||||
} else if (indexPath.section > anchor.section && indexPath.row == 0) {
|
||||
if (anchor.row != [_dataController numberOfRowsInSection:anchor.section] -1) {
|
||||
if (anchor.row != [_dataController completedNumberOfRowsInSection:anchor.section] -1) {
|
||||
return NO; // anchor is not at the end of the section
|
||||
}
|
||||
|
||||
NSInteger nextSection = anchor.section+1;
|
||||
while([_dataController numberOfRowsInSection:nextSection] == 0) {
|
||||
while([_dataController completedNumberOfRowsInSection:nextSection] == 0) {
|
||||
++nextSection;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,10 +31,8 @@
|
||||
* @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.
|
||||
*
|
||||
* @param dataControllerClass A controller class injected to and used to create a data controller for the table view.
|
||||
*
|
||||
* @param ownedByNode Indicates whether the tableView is owned by an ASTableNode.
|
||||
*/
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass ownedByNode:(BOOL)ownedByNode;
|
||||
- (instancetype)_initWithFrame:(CGRect)frame style:(UITableViewStyle)style dataControllerClass:(Class)dataControllerClass;
|
||||
|
||||
/// Set YES and we'll log every time we call [super insertRows…] etc
|
||||
@property (nonatomic) BOOL test_enableSuperUpdateCallLogging;
|
||||
|
||||
@@ -13,17 +13,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a subset of UITableViewDataSource.
|
||||
*
|
||||
* @see ASTableViewDataSource
|
||||
* @see ASTableDataSource
|
||||
*/
|
||||
@protocol ASCommonTableViewDataSource <NSObject>
|
||||
|
||||
@required
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
|
||||
@protocol ASCommonTableDataSource <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
|
||||
@@ -45,7 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* This is a subset of UITableViewDelegate.
|
||||
*
|
||||
* @see ASTableViewDelegate
|
||||
* @see ASTableDelegate
|
||||
*/
|
||||
@protocol ASCommonTableViewDelegate <NSObject, UIScrollViewDelegate>
|
||||
|
||||
@@ -64,14 +62,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
@@ -87,9 +85,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath;
|
||||
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender;
|
||||
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath ASDISPLAYNODE_DEPRECATED;
|
||||
- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED;
|
||||
- (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
- (void)prepareForReloadDataWithSectionCount:(NSInteger)newSectionCount
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, newSectionCount)];
|
||||
|
||||
[_sections removeAllObjects];
|
||||
@@ -97,6 +98,7 @@
|
||||
|
||||
- (void)prepareForInsertSections:(NSIndexSet *)sections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self _populatePendingSectionsFromDataSource:sections];
|
||||
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
@@ -163,6 +165,7 @@
|
||||
|
||||
- (void)prepareForInsertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
LOG(@"Populating elements of kind: %@, for index paths: %@", kind, indexPaths);
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
@@ -184,6 +187,7 @@
|
||||
|
||||
- (void)prepareForDeleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
for (NSString *kind in [self supplementaryKinds]) {
|
||||
NSMutableArray<ASIndexedNodeContext *> *contexts = [NSMutableArray array];
|
||||
[self _populateSupplementaryNodesOfKind:kind atIndexPaths:indexPaths mutableContexts:contexts];
|
||||
@@ -277,7 +281,8 @@
|
||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||
ASIndexedNodeContext *context = [[ASIndexedNodeContext alloc] initWithNodeBlock:supplementaryCellBlock
|
||||
indexPath:indexPath
|
||||
constrainedSize:constrainedSize
|
||||
supplementaryElementKind:kind
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection];
|
||||
[contexts addObject:context];
|
||||
}
|
||||
@@ -289,6 +294,7 @@
|
||||
if ([kind isEqualToString:ASDataControllerRowNodeKind]) {
|
||||
return [super constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
|
||||
} else {
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class ASRangeController;
|
||||
|
||||
@interface ASCollectionView ()
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator ownedByNode:(BOOL)ownedByNode;
|
||||
- (instancetype)_initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout layoutFacilitator:(nullable id<ASCollectionViewLayoutFacilitatorProtocol>)layoutFacilitator;
|
||||
|
||||
@property (nonatomic, weak, readwrite) ASCollectionNode *collectionNode;
|
||||
@property (nonatomic, strong, readonly) ASDataController *dataController;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#import "ASCollectionView.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
|
||||
#define kDefaultItemSize CGSizeMake(50, 50)
|
||||
|
||||
@@ -62,7 +63,11 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath) {
|
||||
// TODO: Handle collection node
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
// With 2.0 `collectionView:constrainedSizeForNodeAtIndexPath:` was moved to the delegate. Assert if not implemented on the delegate but on the data source
|
||||
ASDisplayNodeAssert([collectionView.asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] == NO, @"collectionView:constrainedSizeForNodeAtIndexPath: was moved from the ASCollectionDataSource to the ASCollectionDelegate.");
|
||||
@@ -145,7 +150,10 @@ static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [collectionView.asyncDelegate collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath];
|
||||
#pragma clang diagnostic pop
|
||||
} else {
|
||||
// With 2.0 `collectionView:constrainedSizeForNodeAtIndexPath:` was moved to the delegate. Assert if not implemented on the delegate but on the data source
|
||||
ASDisplayNodeAssert([collectionView.asyncDataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)] == NO, @"collectionView:constrainedSizeForNodeAtIndexPath: was moved from the ASCollectionDataSource to the ASCollectionDelegate.");
|
||||
|
||||
@@ -181,8 +181,25 @@ FOUNDATION_EXPORT NSString * const ASDataControllerRowNodeKind;
|
||||
|
||||
- (nullable ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
- (NSUInteger)completedNumberOfSections;
|
||||
|
||||
- (NSUInteger)completedNumberOfRowsInSection:(NSUInteger)section;
|
||||
|
||||
- (nullable ASCellNode *)nodeAtCompletedIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
/**
|
||||
* @return The index path, in the data source's index space, for the given node.
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
|
||||
|
||||
/**
|
||||
* @return The index path, in UIKit's index space, for the given node.
|
||||
*
|
||||
* @discussion @c indexPathForNode: is returns an index path in the data source's index space.
|
||||
* This method is useful for e.g. looking up the cell for a given node.
|
||||
*/
|
||||
- (nullable NSIndexPath *)completedIndexPathForNode:(ASCellNode *)cellNode;
|
||||
|
||||
/**
|
||||
* Direct access to the nodes that have completed calculation and layout
|
||||
*/
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#import "ASIndexedNodeContext.h"
|
||||
#import "ASDataController+Subclasses.h"
|
||||
#import "ASDispatch.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
|
||||
#import <sys/kdebug_signpost.h>
|
||||
|
||||
@@ -45,6 +47,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
#endif
|
||||
|
||||
@interface ASDataController () {
|
||||
NSMutableDictionary *_nodeContexts; // Main thread only. This is modified immediately during edits i.e. these are in the dataSource's index space.
|
||||
NSMutableArray *_externalCompletedNodes; // Main thread only. External data access can immediately query this if available.
|
||||
NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
|
||||
NSMutableDictionary *_editingNodes; // Modified on _editingTransactionQueue only. Updates propagated to _completedNodes.
|
||||
@@ -79,9 +82,11 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
_dataSource = dataSource;
|
||||
|
||||
_nodeContexts = [NSMutableDictionary dictionary];
|
||||
_completedNodes = [NSMutableDictionary dictionary];
|
||||
_editingNodes = [NSMutableDictionary dictionary];
|
||||
|
||||
_nodeContexts[ASDataControllerRowNodeKind] = [NSMutableArray array];
|
||||
_completedNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
|
||||
_editingNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
|
||||
|
||||
@@ -195,7 +200,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
// Allocate the node.
|
||||
ASIndexedNodeContext *context = contexts[i];
|
||||
ASCellNode *node = [context allocateNode];
|
||||
ASCellNode *node = context.node;
|
||||
if (node == nil) {
|
||||
ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource);
|
||||
node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps.
|
||||
@@ -252,6 +257,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(ASDataControllerCompletionBlock)completionBlock
|
||||
{
|
||||
ASSERT_ON_EDITING_QUEUE;
|
||||
if (!indexPaths.count || _dataSource == nil) {
|
||||
return;
|
||||
}
|
||||
@@ -271,6 +277,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock
|
||||
{
|
||||
ASSERT_ON_EDITING_QUEUE;
|
||||
if (!indexSet.count|| _dataSource == nil) {
|
||||
return;
|
||||
}
|
||||
@@ -294,6 +301,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock
|
||||
{
|
||||
ASSERT_ON_EDITING_QUEUE;
|
||||
if (!indexSet.count || _dataSource == nil) {
|
||||
return;
|
||||
}
|
||||
@@ -403,8 +411,17 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
[self invalidateDataSourceItemCounts];
|
||||
NSUInteger sectionCount = [self itemCountsFromDataSource].size();
|
||||
NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)];
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateNodeContextsFromDataSourceForSections:sectionIndexes];
|
||||
|
||||
NSArray<ASIndexedNodeContext *> *newContexts = [self _populateNodeContextsFromDataSourceForSections:sectionIndexes];
|
||||
|
||||
// Update _nodeContexts
|
||||
NSMutableArray *allContexts = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
[allContexts removeAllObjects];
|
||||
NSArray *nodeIndexPaths = [ASIndexedNodeContext indexPathsFromContexts:newContexts];
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
[allContexts addObject:[[NSMutableArray alloc] init]];
|
||||
}
|
||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(allContexts, nodeIndexPaths, newContexts);
|
||||
|
||||
// Allow subclasses to perform setup before going into the edit transaction
|
||||
[self prepareForReloadDataWithSectionCount:sectionCount];
|
||||
|
||||
@@ -430,7 +447,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
}
|
||||
[self _insertSections:sections atIndexSet:sectionIndexes withAnimationOptions:animationOptions];
|
||||
|
||||
[self _batchLayoutAndInsertNodesFromContexts:contexts withAnimationOptions:animationOptions];
|
||||
[self _batchLayoutAndInsertNodesFromContexts:newContexts withAnimationOptions:animationOptions];
|
||||
|
||||
if (completion) {
|
||||
[_mainSerialQueue performBlockOnMainThread:completion];
|
||||
@@ -476,6 +493,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
||||
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
||||
indexPath:indexPath
|
||||
supplementaryElementKind:nil
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection]];
|
||||
}
|
||||
@@ -564,6 +582,17 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
|
||||
NSArray<ASIndexedNodeContext *> *contexts = [self _populateNodeContextsFromDataSourceForSections:sections];
|
||||
|
||||
// Update _nodeContexts
|
||||
{
|
||||
NSMutableArray *sectionArray = [NSMutableArray arrayWithCapacity:sections.count];
|
||||
for (NSUInteger i = 0; i < sections.count; i++) {
|
||||
[sectionArray addObject:[NSMutableArray array]];
|
||||
}
|
||||
NSMutableArray *allRowContexts = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
[allRowContexts insertObjects:sectionArray atIndexes:sections];
|
||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(allRowContexts, [ASIndexedNodeContext indexPathsFromContexts:contexts], contexts);
|
||||
}
|
||||
|
||||
[self prepareForInsertSections:sections];
|
||||
|
||||
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
|
||||
@@ -589,6 +618,8 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
return;
|
||||
}
|
||||
|
||||
[_nodeContexts[ASDataControllerRowNodeKind] removeObjectsAtIndexes:sections];
|
||||
|
||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
|
||||
[self willDeleteSections:sections];
|
||||
@@ -616,6 +647,11 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray *rowContexts = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
NSArray *contexts = rowContexts[section];
|
||||
[rowContexts removeObjectAtIndex:section];
|
||||
[rowContexts insertObject:contexts atIndex:section];
|
||||
|
||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
|
||||
[self willMoveSection:section toSection:newSection];
|
||||
@@ -717,10 +753,12 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:ASDataControllerRowNodeKind atIndexPath:indexPath];
|
||||
[contexts addObject:[[ASIndexedNodeContext alloc] initWithNodeBlock:nodeBlock
|
||||
indexPath:indexPath
|
||||
supplementaryElementKind:nil
|
||||
constrainedSize:constrainedSize
|
||||
environmentTraitCollection:environmentTraitCollection]];
|
||||
}
|
||||
|
||||
ASInsertElementsIntoMultidimensionalArrayAtIndexPaths(_nodeContexts[ASDataControllerRowNodeKind], sortedIndexPaths, contexts);
|
||||
[self prepareForInsertRowsAtIndexPaths:indexPaths];
|
||||
|
||||
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
|
||||
@@ -747,6 +785,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
// FIXME: Shouldn't deletes be sorted in descending order?
|
||||
NSArray *sortedIndexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)];
|
||||
|
||||
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_nodeContexts[ASDataControllerRowNodeKind], sortedIndexPaths);
|
||||
[self prepareForDeleteRowsAtIndexPaths:sortedIndexPaths];
|
||||
|
||||
dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{
|
||||
@@ -813,6 +852,11 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray *contexts = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
ASIndexedNodeContext *context = contexts[indexPath.section][indexPath.item];
|
||||
[contexts[indexPath.section] removeObjectAtIndex:indexPath.item];
|
||||
[contexts[newIndexPath.section] insertObject:context atIndex:newIndexPath.item];
|
||||
|
||||
LOG(@"Edit Command - moveRow: %@ > %@", indexPath, newIndexPath);
|
||||
dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER);
|
||||
|
||||
@@ -851,10 +895,23 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
- (NSUInteger)numberOfSections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return [[self completedNodes] count];
|
||||
return [_nodeContexts[ASDataControllerRowNodeKind] count];
|
||||
}
|
||||
|
||||
- (NSUInteger)numberOfRowsInSection:(NSUInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
NSArray *contextSections = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
return (section < contextSections.count) ? [contextSections[section] count] : 0;
|
||||
}
|
||||
|
||||
- (NSUInteger)completedNumberOfSections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return [[self completedNodes] count];
|
||||
}
|
||||
|
||||
- (NSUInteger)completedNumberOfRowsInSection:(NSUInteger)section
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
NSArray *completedNodes = [self completedNodes];
|
||||
@@ -865,28 +922,88 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
NSArray *contexts = _nodeContexts[ASDataControllerRowNodeKind];
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
ASIndexedNodeContext *context = nil;
|
||||
|
||||
if (section >= 0 && row >= 0 && section < contexts.count) {
|
||||
NSArray *completedNodesSection = contexts[section];
|
||||
if (row < completedNodesSection.count) {
|
||||
context = completedNodesSection[row];
|
||||
}
|
||||
}
|
||||
|
||||
return context.node;
|
||||
}
|
||||
|
||||
- (ASCellNode *)nodeAtCompletedIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
NSArray *completedNodes = [self completedNodes];
|
||||
NSInteger section = indexPath.section;
|
||||
NSInteger row = indexPath.row;
|
||||
ASCellNode *node = nil;
|
||||
|
||||
|
||||
if (section >= 0 && row >= 0 && section < completedNodes.count) {
|
||||
NSArray *completedNodesSection = completedNodes[section];
|
||||
if (row < completedNodesSection.count) {
|
||||
node = completedNodesSection[row];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode;
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if (cellNode == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSString *kind = cellNode.supplementaryElementKind ?: ASDataControllerRowNodeKind;
|
||||
NSArray *contexts = _nodeContexts[kind];
|
||||
|
||||
// Check if the cached index path is still correct.
|
||||
NSIndexPath *indexPath = cellNode.cachedIndexPath;
|
||||
if (indexPath != nil) {
|
||||
ASIndexedNodeContext *context = ASGetElementInTwoDimensionalArray(contexts, indexPath);
|
||||
if (context.nodeIfAllocated == cellNode) {
|
||||
return indexPath;
|
||||
} else {
|
||||
indexPath = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through each section to look for the node context
|
||||
NSInteger section = 0;
|
||||
for (NSArray<ASIndexedNodeContext *> *nodeContexts in contexts) {
|
||||
NSUInteger item = [nodeContexts indexOfObjectPassingTest:^BOOL(ASIndexedNodeContext * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
return obj.nodeIfAllocated == cellNode;
|
||||
}];
|
||||
if (item != NSNotFound) {
|
||||
indexPath = [NSIndexPath indexPathForItem:item inSection:section];
|
||||
break;
|
||||
}
|
||||
section += 1;
|
||||
}
|
||||
cellNode.cachedIndexPath = indexPath;
|
||||
return indexPath;
|
||||
}
|
||||
|
||||
- (NSIndexPath *)completedIndexPathForNode:(ASCellNode *)cellNode
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (cellNode == nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSInteger section = 0;
|
||||
// Loop through each section to look for the cellNode
|
||||
for (NSArray *sectionNodes in [self completedNodes]) {
|
||||
NSString *kind = cellNode.supplementaryElementKind ?: ASDataControllerRowNodeKind;
|
||||
for (NSArray *sectionNodes in [self completedNodesOfKind:kind]) {
|
||||
NSUInteger item = [sectionNodes indexOfObjectIdenticalTo:cellNode];
|
||||
if (item != NSNotFound) {
|
||||
return [NSIndexPath indexPathForItem:item inSection:section];
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
//
|
||||
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASTableView.h"
|
||||
#import "ASCollectionView.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASAssert.h"
|
||||
|
||||
@implementation ASTableViewProxy
|
||||
@@ -22,6 +22,18 @@
|
||||
selector == @selector(tableView:cellForRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:heightForRowAtIndexPath:) ||
|
||||
|
||||
// Selection, highlighting, menu
|
||||
selector == @selector(tableView:willSelectRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:didSelectRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:willDeselectRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:didDeselectRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:shouldHighlightRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:didHighlightRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:didUnhighlightRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:shouldShowMenuForRowAtIndexPath:) ||
|
||||
selector == @selector(tableView:canPerformAction:forRowAtIndexPath:withSender:) ||
|
||||
selector == @selector(tableView:performAction:forRowAtIndexPath:withSender:) ||
|
||||
|
||||
// handled by ASRangeController
|
||||
selector == @selector(numberOfSectionsInTableView:) ||
|
||||
selector == @selector(tableView:numberOfRowsInSection:) ||
|
||||
@@ -58,13 +70,27 @@
|
||||
selector == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
|
||||
|
||||
// handled by ASRangeController
|
||||
// Selection, highlighting, menu
|
||||
selector == @selector(collectionView:shouldSelectItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:didSelectItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:shouldDeselectItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:didDeselectItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:shouldHighlightItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:didHighlightItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:didUnhighlightItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:shouldShowMenuForItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:canPerformAction:forItemAtIndexPath:withSender:) ||
|
||||
selector == @selector(collectionView:performAction:forItemAtIndexPath:withSender:) ||
|
||||
|
||||
// Item counts
|
||||
selector == @selector(numberOfSectionsInCollectionView:) ||
|
||||
selector == @selector(collectionView:numberOfItemsInSection:) ||
|
||||
|
||||
// used for ASRangeController visibility updates
|
||||
// Element appearance callbacks
|
||||
selector == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:) ||
|
||||
selector == @selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:) ||
|
||||
|
||||
// used for batch fetching API
|
||||
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ||
|
||||
@@ -78,9 +104,7 @@
|
||||
|
||||
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
|
||||
selector == @selector(collectionView:canMoveItemAtIndexPath:) ||
|
||||
selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:) ||
|
||||
selector == @selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:) ||
|
||||
selector == @selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:)
|
||||
selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,22 +13,38 @@
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
#import <AsyncDisplayKit/ASEnvironment.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASIndexedNodeContext : NSObject
|
||||
|
||||
/**
|
||||
* The index path at which this node was originally inserted. Don't rely on this
|
||||
* property too heavily – we should remove it in the future.
|
||||
*/
|
||||
@property (nonatomic, readonly, strong) NSIndexPath *indexPath;
|
||||
@property (nonatomic, readonly, copy, nullable) NSString *supplementaryElementKind;
|
||||
@property (nonatomic, readonly, assign) ASSizeRange constrainedSize;
|
||||
@property (nonatomic, readonly, assign) ASEnvironmentTraitCollection environmentTraitCollection;
|
||||
|
||||
- (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock
|
||||
indexPath:(NSIndexPath *)indexPath
|
||||
supplementaryElementKind:(nullable NSString *)supplementaryElementKind
|
||||
constrainedSize:(ASSizeRange)constrainedSize
|
||||
environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection;
|
||||
|
||||
/**
|
||||
* Returns a node allocated by executing node block. Node block will be nil out immediately.
|
||||
* @return The node, running the node block if necessary. The node block will be discarded
|
||||
* after the first time it is run.
|
||||
*/
|
||||
- (ASCellNode *)allocateNode;
|
||||
@property (strong, readonly) ASCellNode *node;
|
||||
|
||||
/**
|
||||
* @return The node, if the node block has been run already.
|
||||
*/
|
||||
@property (strong, readonly, nullable) ASCellNode *nodeIfAllocated;
|
||||
|
||||
+ (NSArray<NSIndexPath *> *)indexPathsFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
|
||||
#import "ASIndexedNodeContext.h"
|
||||
#import "ASEnvironmentInternal.h"
|
||||
#import "ASCellNode.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
#import <mutex>
|
||||
|
||||
@interface ASIndexedNodeContext ()
|
||||
|
||||
@@ -21,10 +22,14 @@
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASIndexedNodeContext
|
||||
@implementation ASIndexedNodeContext {
|
||||
std::mutex _lock;
|
||||
ASCellNode *_node;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock
|
||||
indexPath:(NSIndexPath *)indexPath
|
||||
supplementaryElementKind:(nullable NSString *)supplementaryElementKind
|
||||
constrainedSize:(ASSizeRange)constrainedSize
|
||||
environmentTraitCollection:(ASEnvironmentTraitCollection)environmentTraitCollection
|
||||
{
|
||||
@@ -33,19 +38,35 @@
|
||||
if (self) {
|
||||
_nodeBlock = nodeBlock;
|
||||
_indexPath = indexPath;
|
||||
_supplementaryElementKind = [supplementaryElementKind copy];
|
||||
_constrainedSize = constrainedSize;
|
||||
_environmentTraitCollection = environmentTraitCollection;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASCellNode *)allocateNode
|
||||
- (ASCellNode *)node
|
||||
{
|
||||
NSAssert(_nodeBlock != nil, @"Node block is gone. Should not execute it more than once");
|
||||
ASCellNode *node = _nodeBlock();
|
||||
_nodeBlock = nil;
|
||||
ASEnvironmentStatePropagateDown(node, _environmentTraitCollection);
|
||||
return node;
|
||||
std::lock_guard<std::mutex> l(_lock);
|
||||
if (_nodeBlock != nil) {
|
||||
ASCellNode *node = _nodeBlock();
|
||||
_nodeBlock = nil;
|
||||
if (node == nil) {
|
||||
ASDisplayNodeFailAssert(@"Node block returned nil node! Index path: %@", _indexPath);
|
||||
node = [[ASCellNode alloc] init];
|
||||
}
|
||||
node.cachedIndexPath = _indexPath;
|
||||
node.supplementaryElementKind = _supplementaryElementKind;
|
||||
ASEnvironmentStatePropagateDown(node, _environmentTraitCollection);
|
||||
_node = node;
|
||||
}
|
||||
return _node;
|
||||
}
|
||||
|
||||
- (ASCellNode *)nodeIfAllocated
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_lock);
|
||||
return _node;
|
||||
}
|
||||
|
||||
+ (NSArray<NSIndexPath *> *)indexPathsFromContexts:(NSArray<ASIndexedNodeContext *> *)contexts
|
||||
|
||||
267
AsyncDisplayKit/Private/ASCollectionView+Undeprecated.h
Normal file
267
AsyncDisplayKit/Private/ASCollectionView+Undeprecated.h
Normal file
@@ -0,0 +1,267 @@
|
||||
//
|
||||
// ASCollectionView+Undeprecated.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Adlai Holler on 10/10/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Currently our public collection API is on @c ASCollectionNode and the @c ASCollectionView
|
||||
* API is deprecated, but the implementations still live in the view.
|
||||
*
|
||||
* This category lets us avoid deprecation warnings everywhere internally.
|
||||
* In the future, the @c ASCollectionView public API will be eliminated and so will this file.
|
||||
*/
|
||||
@interface ASCollectionView (Undeprecated)
|
||||
|
||||
/**
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified layout.
|
||||
*
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
/**
|
||||
* Initializes an ASCollectionView
|
||||
*
|
||||
* @discussion Initializes and returns a newly allocated collection view object with the specified frame and layout.
|
||||
*
|
||||
* @param frame The frame rectangle for the collection view, measured in points. The origin of the frame is relative to the superview in which you plan to add it. This frame is passed to the superclass during initialization.
|
||||
* @param layout The layout object to use for organizing items. The collection view stores a strong reference to the specified object. Must not be nil.
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in full mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param rangeMode The range mode to get the running parameters for.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in the given mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeMode The range mode to set the running parameters for.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously, optionally disabling all animations in the batch. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before the update block completes.
|
||||
*
|
||||
* @param animated NO to disable animations for this batch
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Perform a batch of updates asynchronously. This method must be called from the main thread.
|
||||
* The asyncDataSource must be updated to reflect the changes before update block completes.
|
||||
*
|
||||
* @param updates The block that performs the relevant insert, delete, reload, or move operations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)performBatchUpdates:(nullable __attribute((noescape)) void (^)())updates completion:(nullable void (^)(BOOL finished))completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadDataWithCompletion:(nullable void (^)())completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version.
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UICollectionView's version and will block the main thread
|
||||
* while all the cells load.
|
||||
*/
|
||||
- (void)reloadDataImmediately;
|
||||
|
||||
/**
|
||||
* Triggers a relayout of all nodes.
|
||||
*
|
||||
* @discussion This method invalidates and lays out every cell node in the collection.
|
||||
*/
|
||||
- (void)relayoutItems;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted;
|
||||
|
||||
/**
|
||||
* Registers the given kind of supplementary node for use in creating node-backed supplementary views.
|
||||
*
|
||||
* @param elementKind The kind of supplementary node that will be requested through the data source.
|
||||
*
|
||||
* @discussion Use this method to register support for the use of supplementary nodes in place of the default
|
||||
* `registerClass:forSupplementaryViewOfKind:withReuseIdentifier:` and `registerNib:forSupplementaryViewOfKind:withReuseIdentifier:`
|
||||
* methods. This method will register an internal backing view that will host the contents of the supplementary nodes
|
||||
* returned from the data source.
|
||||
*/
|
||||
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Inserts items at the locations identified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing an item index and section index that together identify an item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Deletes the items specified by an array of index paths.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to delete.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Reloads the specified items.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the items to reload.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
/**
|
||||
* Moves the item at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the item to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the item.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a cellNode in the collection view
|
||||
*
|
||||
* @return The index path for this cell node.
|
||||
*
|
||||
* @discussion This index path returned by this method is in the _view's_ index space
|
||||
* and should only be used with @c ASCollectionView directly. To get an index path suitable
|
||||
* for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the
|
||||
* collection node instead.
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -71,5 +71,10 @@ extern NSArray *ASIndexPathsForTwoDimensionalArray(NSArray <NSArray *>* twoDimen
|
||||
*/
|
||||
extern NSArray *ASIndexPathsForMultidimensionalArray(NSArray *MultidimensionalArray) AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Attempt to get the object at the given index path. Returns @c nil if the index path is out of bounds.
|
||||
*/
|
||||
extern id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) AS_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
@@ -223,3 +223,19 @@ NSArray *ASIndexPathsForMultidimensionalArray(NSArray *multidimensionalArray)
|
||||
ASRecursivelyFindIndexPathsForMultidimensionalArray(multidimensionalArray, [[NSIndexPath alloc] init], res);
|
||||
return res;
|
||||
}
|
||||
|
||||
id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath)
|
||||
{
|
||||
ASDisplayNodeCAssert(indexPath.length == 2, @"Expected index path of length 2. Index path: %@", indexPath);
|
||||
NSInteger section = indexPath.section;
|
||||
if (array.count <= section) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray *innerArray = array[section];
|
||||
NSInteger item = indexPath.item;
|
||||
if (innerArray.count <= item) {
|
||||
return nil;
|
||||
}
|
||||
return innerArray[item];
|
||||
}
|
||||
|
||||
273
AsyncDisplayKit/Private/ASTableView+Undeprecated.h
Normal file
273
AsyncDisplayKit/Private/ASTableView+Undeprecated.h
Normal file
@@ -0,0 +1,273 @@
|
||||
//
|
||||
// ASTableView+Undeprecated.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Adlai Holler on 10/10/16.
|
||||
// Copyright © 2016 Facebook. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* Currently our public table API is on @c ASTableNode and the @c ASTableView
|
||||
* API is deprecated, but the implementations still live in the view.
|
||||
*
|
||||
* This category lets us avoid deprecation warnings everywhere internally.
|
||||
* In the future, the ASTableView public API will be eliminated and so will this file.
|
||||
*/
|
||||
@interface ASTableView (Undeprecated)
|
||||
|
||||
/**
|
||||
* Initializer.
|
||||
*
|
||||
* @param frame A rectangle specifying the initial location and size of the table view in its superview’s coordinates.
|
||||
* The frame of the table view changes as table cells are added and deleted.
|
||||
*
|
||||
* @param style A constant that specifies the style of the table view. See UITableViewStyle for descriptions of valid constants.
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in full mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in full mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param rangeMode The range mode to get the running parameters for.
|
||||
* @param rangeType The range type to get the tuning parameters for.
|
||||
*
|
||||
* @return A tuning parameter value for the given range type in the given mode.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Set the tuning parameters for a range type in the specified mode.
|
||||
*
|
||||
* @param tuningParameters The tuning parameters to store for a range type.
|
||||
* @param rangeMode The range mode to set the running parameters for.
|
||||
* @param rangeType The range type to set the tuning parameters for.
|
||||
*
|
||||
* @see ASLayoutRangeMode
|
||||
* @see ASLayoutRangeType
|
||||
*/
|
||||
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
|
||||
|
||||
/**
|
||||
* Similar to -visibleCells.
|
||||
*
|
||||
* @return an array containing the cell nodes being displayed on screen.
|
||||
*/
|
||||
- (NSArray<ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Similar to -indexPathForCell:.
|
||||
*
|
||||
* @param cellNode a cellNode part of the table view
|
||||
*
|
||||
* @return an indexPath for this cellNode
|
||||
*/
|
||||
- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* The number of screens left to scroll before the delegate -tableView:beginBatchFetchingWithContext: is called.
|
||||
*
|
||||
* Defaults to two screenfuls.
|
||||
*/
|
||||
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @param completion block to run on completion of asynchronous loading or nil. If supplied, the block is run on
|
||||
* the main thread.
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
-(void)reloadDataWithCompletion:(void (^ _Nullable)())completion;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UITableView's version.
|
||||
*/
|
||||
- (void)reloadData;
|
||||
|
||||
/**
|
||||
* Reload everything from scratch entirely on the main thread, destroying the working range and all cached nodes.
|
||||
*
|
||||
* @warning This method is substantially more expensive than UITableView's version and will block the main thread while
|
||||
* all the cells load.
|
||||
*/
|
||||
- (void)reloadDataImmediately;
|
||||
|
||||
/**
|
||||
* Triggers a relayout of all nodes.
|
||||
*
|
||||
* @discussion This method invalidates and lays out every cell node in the table view.
|
||||
*/
|
||||
- (void)relayoutItems;
|
||||
|
||||
/**
|
||||
* Begins a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block.
|
||||
*
|
||||
* @discussion You call this method to bracket a series of method calls that ends with endUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. It's important to remember that the ASTableView will be processing the updates asynchronously after this call is completed.
|
||||
*
|
||||
* @warning This method must be called from the main thread.
|
||||
*/
|
||||
- (void)beginUpdates;
|
||||
|
||||
/**
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view, with animation enabled and no completion block.
|
||||
*
|
||||
* @discussion You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. It's important to remember that the ASTableView will be processing the updates asynchronously after this call is completed.
|
||||
*
|
||||
* @warning This method is must be called from the main thread.
|
||||
*/
|
||||
- (void)endUpdates;
|
||||
|
||||
/**
|
||||
* Concludes a series of method calls that insert, delete, select, or reload rows and sections of the table view.
|
||||
* You call this method to bracket a series of method calls that begins with beginUpdates and that consists of operations
|
||||
* to insert, delete, select, and reload rows and sections of the table view. When you call endUpdates, ASTableView begins animating
|
||||
* the operations simultaneously. This method is must be called from the main thread. It's important to remember that the ASTableView will
|
||||
* be processing the updates asynchronously after this call and are not guaranteed to be reflected in the ASTableView until
|
||||
* the completion block is executed.
|
||||
*
|
||||
* @param animated NO to disable all animations.
|
||||
* @param completion A completion handler block to execute when all of the operations are finished. This block takes a single
|
||||
* Boolean parameter that contains the value YES if all of the related animations completed successfully or
|
||||
* NO if they were interrupted. This parameter may be nil. If supplied, the block is run on the main thread.
|
||||
*/
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL completed))completion;
|
||||
|
||||
/**
|
||||
* Blocks execution of the main thread until all section and row updates are committed. This method must be called from the main thread.
|
||||
*/
|
||||
- (void)waitUntilAllUpdatesAreCommitted;
|
||||
|
||||
/**
|
||||
* Inserts one or more sections, with an option to animate the insertion.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to insert.
|
||||
*
|
||||
* @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Deletes one or more sections, with an option to animate the deletion.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to delete.
|
||||
*
|
||||
* @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Reloads the specified sections using a given animation effect.
|
||||
*
|
||||
* @param sections An index set that specifies the sections to reload.
|
||||
*
|
||||
* @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Moves a section to a new location.
|
||||
*
|
||||
* @param section The index of the section to move.
|
||||
*
|
||||
* @param newSection The index that is the destination of the move for the section.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection;
|
||||
|
||||
/**
|
||||
* Inserts rows at the locations identified by an array of index paths, with an option to animate the insertion.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects, each representing a row index and section index that together identify a row.
|
||||
*
|
||||
* @param animation A constant that indicates how the insertion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Deletes the rows specified by an array of index paths, with an option to animate the deletion.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the rows to delete.
|
||||
*
|
||||
* @param animation A constant that indicates how the deletion is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Reloads the specified rows using a given animation effect.
|
||||
*
|
||||
* @param indexPaths An array of NSIndexPath objects identifying the rows to reload.
|
||||
*
|
||||
* @param animation A constant that indicates how the reloading is to be animated. See UITableViewRowAnimation.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
|
||||
|
||||
/**
|
||||
* Moves the row at a specified location to a destination location.
|
||||
*
|
||||
* @param indexPath The index path identifying the row to move.
|
||||
*
|
||||
* @param newIndexPath The index path that is the destination of the move for the row.
|
||||
*
|
||||
* @discussion This method must be called from the main thread. The asyncDataSource must be updated to reflect the changes
|
||||
* before this method is called.
|
||||
*/
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
|
||||
|
||||
@end
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -13,13 +13,20 @@
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
#import "ASCollectionView.h"
|
||||
#import "ASCollectionNode.h"
|
||||
#import "ASCollectionViewFlowLayoutInspector.h"
|
||||
#import "ASCellNode.h"
|
||||
|
||||
@interface ASCollectionView (Private)
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Test Data Source
|
||||
*/
|
||||
@interface InspectorTestDataSource : NSObject <ASCollectionViewDataSource>
|
||||
@interface InspectorTestDataSource : NSObject <ASCollectionDataSource>
|
||||
@end
|
||||
|
||||
@implementation InspectorTestDataSource
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#import "ASSectionContext.h"
|
||||
#import <vector>
|
||||
#import <OCMock/OCMock.h>
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
|
||||
@interface ASTextCellNodeWithSetSelectedCounter : ASTextCellNode
|
||||
|
||||
@@ -54,7 +55,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewTestDelegate : NSObject <ASCollectionViewDataSource, ASCollectionViewDelegate>
|
||||
@interface ASCollectionViewTestDelegate : NSObject <ASCollectionDataSource, ASCollectionDelegate, UICollectionViewDelegateFlowLayout>
|
||||
|
||||
@property (nonatomic, assign) NSInteger sectionGeneration;
|
||||
|
||||
@@ -110,7 +111,7 @@
|
||||
return _itemCounts[section];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)collectionView:(ASCollectionView *)collectionView contextForSection:(NSInteger)section
|
||||
- (id<ASSectionContext>)collectionNode:(ASCollectionNode *)collectionNode contextForSection:(NSInteger)section
|
||||
{
|
||||
ASTestSectionContext *context = [[ASTestSectionContext alloc] init];
|
||||
context.sectionGeneration = _sectionGeneration;
|
||||
@@ -118,12 +119,23 @@
|
||||
return context;
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
|
||||
{
|
||||
return CGSizeMake(100, 100);
|
||||
}
|
||||
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [[ASCellNode alloc] init];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionViewTestController: UIViewController
|
||||
|
||||
@property (nonatomic, strong) ASCollectionViewTestDelegate *asyncDelegate;
|
||||
@property (nonatomic, strong) ASCollectionView *collectionView;
|
||||
@property (nonatomic, strong) ASCollectionNode *collectionNode;
|
||||
|
||||
@end
|
||||
|
||||
@@ -136,12 +148,13 @@
|
||||
self.asyncDelegate = [[ASCollectionViewTestDelegate alloc] initWithNumberOfSections:10 numberOfItemsInSection:10];
|
||||
id realLayout = [UICollectionViewFlowLayout new];
|
||||
id mockLayout = [OCMockObject partialMockForObject:realLayout];
|
||||
self.collectionView = [[ASCollectionView alloc] initWithFrame:self.view.bounds
|
||||
collectionViewLayout:mockLayout];
|
||||
self.collectionNode = [[ASCollectionNode alloc] initWithFrame:self.view.bounds collectionViewLayout:mockLayout];
|
||||
self.collectionView = self.collectionNode.view;
|
||||
self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.collectionView.asyncDataSource = self.asyncDelegate;
|
||||
self.collectionView.asyncDelegate = self.asyncDelegate;
|
||||
self.collectionNode.dataSource = self.asyncDelegate;
|
||||
self.collectionNode.delegate = self.asyncDelegate;
|
||||
|
||||
[self.collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[self.view addSubview:self.collectionView];
|
||||
}
|
||||
return self;
|
||||
@@ -303,6 +316,7 @@
|
||||
ASCollectionViewTestController *testController = [[ASCollectionViewTestController alloc] initWithNibName:nil bundle:nil];\
|
||||
__unused ASCollectionViewTestDelegate *del = testController.asyncDelegate;\
|
||||
__unused ASCollectionView *cv = testController.collectionView;\
|
||||
__unused ASCollectionNode *cn = testController.collectionNode;\
|
||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];\
|
||||
[window makeKeyAndVisible]; \
|
||||
window.rootViewController = testController;\
|
||||
@@ -404,7 +418,7 @@
|
||||
- (void)testCellNodeLayoutAttributes
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSSet *nodeBatch1 = [NSSet setWithArray:[cv visibleNodes]];
|
||||
NSSet *nodeBatch1 = [NSSet setWithArray:[cn visibleNodes]];
|
||||
XCTAssertGreaterThan(nodeBatch1.count, 0);
|
||||
|
||||
// Expect all visible nodes get 1 applyLayoutAttributes and have a non-nil value.
|
||||
@@ -419,7 +433,7 @@
|
||||
[cv layoutIfNeeded];
|
||||
|
||||
// Ensure we scrolled far enough that all the old ones are offscreen.
|
||||
NSSet *nodeBatch2 = [NSSet setWithArray:[cv visibleNodes]];
|
||||
NSSet *nodeBatch2 = [NSSet setWithArray:[cn visibleNodes]];
|
||||
XCTAssertFalse([nodeBatch1 intersectsSet:nodeBatch2], @"Expected to scroll far away enough that all nodes are replaced.");
|
||||
|
||||
// Now the nodes are no longer visible, expect their layout attributes are nil but not another applyLayoutAttributes call.
|
||||
@@ -438,7 +452,8 @@
|
||||
{
|
||||
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
||||
UICollectionViewLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
ASCollectionView *cv = [[ASCollectionView alloc] initWithFrame:window.bounds collectionViewLayout:layout];
|
||||
ASCollectionNode *cn = [[ASCollectionNode alloc] initWithFrame:window.bounds collectionViewLayout:layout];
|
||||
ASCollectionView *cv = cn.view;
|
||||
|
||||
|
||||
__unused NSMutableSet *keepaliveNodes = [NSMutableSet set];
|
||||
@@ -455,7 +470,7 @@
|
||||
layerBacked.name = [NSString stringWithFormat:@"Subnode #%d", thisNodeIdx];
|
||||
[suppNode addSubnode:layerBacked];
|
||||
[invocation setReturnValue:&suppNode];
|
||||
}] collectionView:cv nodeForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:OCMOCK_ANY];
|
||||
}] collectionNode:cn nodeForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:OCMOCK_ANY];
|
||||
[[[dataSource stub] andReturnValue:[NSNumber numberWithInteger:1]] numberOfSectionsInCollectionView:cv];
|
||||
cv.asyncDataSource = dataSource;
|
||||
|
||||
@@ -483,14 +498,14 @@
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
id layout = cv.collectionViewLayout;
|
||||
CGSize initialItemSize = [cv calculatedSizeForNodeAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
|
||||
CGSize initialItemSize = [cv nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].calculatedSize;
|
||||
CGSize initialCVSize = cv.bounds.size;
|
||||
|
||||
// Capture the node size before first call to prepareLayout after frame change.
|
||||
__block CGSize itemSizeAtFirstLayout = CGSizeZero;
|
||||
__block CGSize boundsSizeAtFirstLayout = CGSizeZero;
|
||||
[[[[layout expect] andDo:^(NSInvocation *) {
|
||||
itemSizeAtFirstLayout = [cv calculatedSizeForNodeAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
|
||||
itemSizeAtFirstLayout = [cv nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].calculatedSize;
|
||||
boundsSizeAtFirstLayout = [cv bounds].size;
|
||||
}] andForwardToRealObject] prepareLayout];
|
||||
|
||||
@@ -498,7 +513,7 @@
|
||||
UIDeviceOrientation oldDeviceOrientation = [[UIDevice currentDevice] orientation];
|
||||
[[UIDevice currentDevice] setValue:@(UIDeviceOrientationLandscapeLeft) forKey:@"orientation"];
|
||||
|
||||
CGSize finalItemSize = [cv calculatedSizeForNodeAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
|
||||
CGSize finalItemSize = [cv nodeForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].calculatedSize;
|
||||
CGSize finalCVSize = cv.bounds.size;
|
||||
XCTAssertNotEqualObjects(NSStringFromCGSize(initialItemSize), NSStringFromCGSize(itemSizeAtFirstLayout));
|
||||
XCTAssertNotEqualObjects(NSStringFromCGSize(initialCVSize), NSStringFromCGSize(boundsSizeAtFirstLayout));
|
||||
@@ -562,7 +577,7 @@
|
||||
updateValidationTestPrologue
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cn contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 1);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
@@ -582,7 +597,7 @@
|
||||
|
||||
// Only test left moving
|
||||
XCTAssertTrue(toSection < originalSection);
|
||||
ASTestSectionContext *movedSectionContext = (ASTestSectionContext *)[cv contextForSection:toSection];
|
||||
ASTestSectionContext *movedSectionContext = (ASTestSectionContext *)[cn 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.
|
||||
@@ -590,7 +605,7 @@
|
||||
XCTAssertEqual(movedSectionContext.sectionIndex, toSection);
|
||||
|
||||
for (NSInteger section = toSection + 1; section <= originalSection && section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cn contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 1);
|
||||
// This section context was shifted to the right
|
||||
@@ -607,7 +622,7 @@
|
||||
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cn contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, 2);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
@@ -625,7 +640,7 @@
|
||||
|
||||
NSInteger sectionCount = del->_itemCounts.size();
|
||||
for (NSInteger section = 0; section < sectionCount; section++) {
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cv contextForSection:section];
|
||||
ASTestSectionContext *context = (ASTestSectionContext *)[cn contextForSection:section];
|
||||
XCTAssertNotNil(context);
|
||||
XCTAssertEqual(context.sectionGeneration, section != sectionToReload ? 1 : 2);
|
||||
XCTAssertEqual(context.sectionIndex, section);
|
||||
@@ -648,4 +663,39 @@
|
||||
XCTAssertNoThrow([cv insertSections:[NSIndexSet indexSetWithIndex:0]]);
|
||||
}
|
||||
|
||||
- (void)testThatNodeAtIndexPathIsCorrectImmediatelyAfterSubmittingUpdate
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
|
||||
|
||||
// Insert an item and assert nodeForItemAtIndexPath: immediately returns new node
|
||||
ASCellNode *oldNode = [cn nodeForItemAtIndexPath:indexPath];
|
||||
XCTAssertNotNil(oldNode);
|
||||
del->_itemCounts[0] += 1;
|
||||
[cv insertItemsAtIndexPaths:@[ indexPath ]];
|
||||
ASCellNode *newNode = [cn nodeForItemAtIndexPath:indexPath];
|
||||
XCTAssertNotNil(newNode);
|
||||
XCTAssertNotEqualObjects(oldNode, newNode);
|
||||
|
||||
// Delete all sections and assert nodeForItemAtIndexPath: immediately returns nil
|
||||
NSIndexSet *sections = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, del->_itemCounts.size())];
|
||||
del->_itemCounts.clear();
|
||||
[cv deleteSections:sections];
|
||||
XCTAssertNil([cn nodeForItemAtIndexPath:indexPath]);
|
||||
}
|
||||
|
||||
- (void)DISABLED_testThatSupplementaryNodeAtIndexPathIsCorrectImmediatelyAfterSubmittingUpdate
|
||||
{
|
||||
updateValidationTestPrologue
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
|
||||
ASCellNode *oldHeader = [cv supplementaryNodeForElementKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
|
||||
XCTAssertNotNil(oldHeader);
|
||||
|
||||
// Reload the section and ensure that the new header is loaded
|
||||
[cv reloadSections:[NSIndexSet indexSetWithIndex:0]];
|
||||
ASCellNode *newHeader = [cv supplementaryNodeForElementKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
|
||||
XCTAssertNotNil(newHeader);
|
||||
XCTAssertNotEqualObjects(oldHeader, newHeader);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCellNode.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
|
||||
#define NumberOfSections 10
|
||||
#define NumberOfRowsPerSection 20
|
||||
@@ -43,7 +44,7 @@
|
||||
|
||||
- (instancetype)__initWithFrame:(CGRect)frame style:(UITableViewStyle)style
|
||||
{
|
||||
return [super _initWithFrame:frame style:style dataControllerClass:[ASTestDataController class] ownedByNode:NO];
|
||||
return [super _initWithFrame:frame style:style dataControllerClass:[ASTestDataController class]];
|
||||
}
|
||||
|
||||
- (ASTestDataController *)testDataController
|
||||
@@ -60,7 +61,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableViewTestDelegate : NSObject <ASTableViewDataSource, ASTableViewDelegate>
|
||||
@interface ASTableViewTestDelegate : NSObject <ASTableDataSource, ASTableDelegate>
|
||||
@property (nonatomic, copy) void (^willDeallocBlock)(ASTableViewTestDelegate *delegate);
|
||||
@end
|
||||
|
||||
@@ -107,7 +108,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableViewFilledDataSource : NSObject <ASTableViewDataSource, ASTableViewDelegate>
|
||||
@interface ASTableViewFilledDataSource : NSObject <ASTableDataSource, ASTableDelegate>
|
||||
@end
|
||||
|
||||
@implementation ASTableViewFilledDataSource
|
||||
@@ -141,7 +142,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASTableViewFilledDelegate : NSObject <ASTableViewDelegate>
|
||||
@interface ASTableViewFilledDelegate : NSObject <ASTableDelegate>
|
||||
@end
|
||||
|
||||
@implementation ASTableViewFilledDelegate
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
@import XCTest;
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import "ASTableViewInternal.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
|
||||
|
||||
// Set to 1 to use UITableView and see if the issue still exists.
|
||||
#define USE_UIKIT_REFERENCE 0
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
|
||||
UICollectionView *cv = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) collectionViewLayout:layoutMock];
|
||||
id dataSource = [OCMockObject niceMockForProtocol:@protocol(UICollectionViewDataSource)];
|
||||
[[[dataSource stub] andReturnValue:[NSNumber numberWithInteger:1]] collectionView:cv numberOfItemsInSection:0];
|
||||
|
||||
cv.dataSource = dataSource;
|
||||
|
||||
@@ -137,4 +136,29 @@
|
||||
XCTAssertThrowsSpecificNamed([cv insertSections:[NSIndexSet indexSetWithIndex:0]], NSException, NSInternalInconsistencyException);
|
||||
}
|
||||
|
||||
// If you put reloadData in a batch update, collection view will ignore it and perform the normal
|
||||
// update validation i.e. throw an exception if your data source counts changed.
|
||||
- (void)testThatPuttingReloadDataInABatchUpdateDoesntWork
|
||||
{
|
||||
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
|
||||
UICollectionView *cv = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) collectionViewLayout:layout];
|
||||
id dataSource = [OCMockObject niceMockForProtocol:@protocol(UICollectionViewDataSource)];
|
||||
// Start data source at 1 section, 1 item
|
||||
[[[dataSource stub] andReturnValue:[NSNumber numberWithInteger:1]] numberOfSectionsInCollectionView:cv];
|
||||
[[[dataSource expect] andReturnValue:[NSNumber numberWithInteger:1]] collectionView:cv numberOfItemsInSection:0];
|
||||
|
||||
cv.dataSource = dataSource;
|
||||
|
||||
// Verify initial data.
|
||||
XCTAssertEqual([cv numberOfSections], 1);
|
||||
XCTAssertEqual([cv numberOfItemsInSection:0], 1);
|
||||
[dataSource verify];
|
||||
|
||||
XCTAssertThrows([cv performBatchUpdates:^{
|
||||
// Change data source to 1 section, 2 items
|
||||
[[[dataSource stub] andReturnValue:[NSNumber numberWithInteger:2]] collectionView:cv numberOfItemsInSection:0];
|
||||
[cv reloadData];
|
||||
} completion:nil]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
@property (nonatomic, strong) NSArray *data;
|
||||
@end
|
||||
|
||||
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
|
||||
@interface ViewController () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout>
|
||||
|
||||
@end
|
||||
|
||||
@@ -49,12 +49,16 @@
|
||||
layout.headerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||
layout.footerReferenceSize = CGSizeMake(50.0, 50.0);
|
||||
|
||||
// This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView.
|
||||
// This functionality & example project remains for users who insist on using ASCollectionView.
|
||||
self.collectionView = [[ASCollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:layout];
|
||||
self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.collectionView.asyncDataSource = self;
|
||||
self.collectionView.asyncDelegate = self;
|
||||
self.collectionView.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
// This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView.
|
||||
// This functionality & example project remains for users who insist on using ASCollectionView.
|
||||
[self.collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[self.collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||
[self.view addSubview:self.collectionView];
|
||||
@@ -92,6 +96,8 @@
|
||||
|
||||
- (void)reloadTapped
|
||||
{
|
||||
// This method is deprecated because we reccommend using ASCollectionNode instead of ASCollectionView.
|
||||
// This functionality & example project remains for users who insist on using ASCollectionView.
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self.navigationItem.title = @"Home";
|
||||
_tableNode = [[ASTableNode alloc] init];
|
||||
_tableNode.delegate = self;
|
||||
_tableNode.dataSource = self;
|
||||
@@ -42,17 +41,18 @@
|
||||
if (!(self = [super initWithNode:_tableNode])) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
[self generateFeedData];
|
||||
self.navigationItem.title = @"Home";
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
[self generateFeedData];
|
||||
|
||||
[_tableNode.view reloadData];
|
||||
[super viewDidLoad];
|
||||
|
||||
[_tableNode reloadData];
|
||||
}
|
||||
|
||||
- (void)generateFeedData
|
||||
@@ -65,18 +65,23 @@
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionDelegate - ASCollectionDataSource
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
|
||||
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return _videoFeedData.count;
|
||||
}
|
||||
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
|
||||
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
|
||||
|
||||
return cellNode;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
_videoFeedData = [[NSMutableArray alloc] initWithObjects:[[VideoModel alloc] init], [[VideoModel alloc] init], nil];
|
||||
|
||||
[_tableNode.view reloadData];
|
||||
[_tableNode reloadData];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
@@ -63,26 +63,24 @@
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionDelegate - ASCollectionDataSource
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableNode:(ASTableNode *)tableNode
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
|
||||
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return _videoFeedData.count;
|
||||
}
|
||||
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNode *)tableNode:(ASTableNode *)tableNode nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
VideoModel *videoObject = [_videoFeedData objectAtIndex:indexPath.row];
|
||||
VideoContentCell *cellNode = [[VideoContentCell alloc] initWithVideoObject:videoObject];
|
||||
return cellNode;
|
||||
}
|
||||
|
||||
//- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath{
|
||||
// CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
|
||||
// return ASSizeRangeMake(CGSizeMake(fullWidth, 0.0), CGSizeMake(fullWidth, 400.0));
|
||||
//}
|
||||
|
||||
- (ASVideoPlayerNode *)videoPlayerNode;
|
||||
{
|
||||
if (_videoPlayerNode) {
|
||||
@@ -216,4 +214,4 @@
|
||||
return mainVerticalStack;
|
||||
}*/
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
[_activityIndicatorView stopAnimating];
|
||||
|
||||
[self insertNewRowsInTableView:newPhotos];
|
||||
[self insertNewRowsInTableNode:newPhotos];
|
||||
// [self requestCommentsForPhotos:newPhotos];
|
||||
|
||||
// immediately start second larger fetch
|
||||
@@ -107,7 +107,7 @@
|
||||
{
|
||||
[_photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){
|
||||
|
||||
[self insertNewRowsInTableView:newPhotos];
|
||||
[self insertNewRowsInTableNode:newPhotos];
|
||||
// [self requestCommentsForPhotos:newPhotos];
|
||||
if (context) {
|
||||
[context completeBatchFetching:YES];
|
||||
@@ -133,7 +133,7 @@
|
||||
// }
|
||||
//}
|
||||
|
||||
- (void)insertNewRowsInTableView:(NSArray *)newPhotos
|
||||
- (void)insertNewRowsInTableNode:(NSArray *)newPhotos
|
||||
{
|
||||
NSInteger section = 0;
|
||||
NSMutableArray *indexPaths = [NSMutableArray array];
|
||||
@@ -144,7 +144,7 @@
|
||||
[indexPaths addObject:path];
|
||||
}
|
||||
|
||||
[_tableNode.view insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
[_tableNode insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
@@ -161,12 +161,12 @@
|
||||
|
||||
#pragma mark - ASTableDataSource methods
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return [_photoFeed numberOfItemsInFeed];
|
||||
}
|
||||
|
||||
- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row];
|
||||
// this will be executed on a background thread - important to make sure it's thread safe
|
||||
@@ -181,7 +181,7 @@
|
||||
#pragma mark - ASTableDelegate methods
|
||||
|
||||
// Receive a message that the tableView is near the end of its data set and more data should be fetched if necessary.
|
||||
- (void)tableView:(ASTableView *)tableView willBeginBatchFetchWithContext:(ASBatchContext *)context
|
||||
- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context
|
||||
{
|
||||
[context beginBatchFetching];
|
||||
[self loadPageWithContext:context];
|
||||
@@ -192,7 +192,7 @@
|
||||
- (void)resetAllData
|
||||
{
|
||||
[_photoFeed clearFeed];
|
||||
[_tableNode.view reloadData];
|
||||
[_tableNode reloadData];
|
||||
[self refreshFeed];
|
||||
}
|
||||
|
||||
|
||||
@@ -38,9 +38,7 @@
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
self.imageNode.position = CGPointZero;
|
||||
self.imageNode.style.preferredSize = constrainedSize.max;
|
||||
return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[self.imageNode]];
|
||||
return [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:_imageNode];
|
||||
}
|
||||
|
||||
- (void)layoutDidFinish
|
||||
|
||||
@@ -16,17 +16,21 @@
|
||||
//
|
||||
|
||||
#import "DetailRootNode.h"
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
#import "DetailCellNode.h"
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
static const NSInteger kImageHeight = 200;
|
||||
|
||||
@interface DetailRootNode () <ASCollectionViewDataSource, ASCollectionViewDelegate>
|
||||
|
||||
@interface DetailRootNode () <ASCollectionDataSource, ASCollectionDelegate>
|
||||
|
||||
@property (nonatomic, copy) NSString *imageCategory;
|
||||
@property (nonatomic, strong) ASCollectionNode *collectionNode;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DetailRootNode
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
@@ -69,12 +73,12 @@ static const NSInteger kImageHeight = 200;
|
||||
|
||||
#pragma mark - ASCollectionDataSource
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSString *imageCategory = self.imageCategory;
|
||||
return ^{
|
||||
@@ -85,9 +89,9 @@ static const NSInteger kImageHeight = 200;
|
||||
};
|
||||
}
|
||||
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
CGSize imageSize = CGSizeMake(CGRectGetWidth(collectionView.frame), kImageHeight);
|
||||
CGSize imageSize = CGSizeMake(CGRectGetWidth(collectionNode.view.frame), kImageHeight);
|
||||
return ASSizeRangeMake(imageSize, imageSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,16 @@
|
||||
|
||||
#import "DetailViewController.h"
|
||||
|
||||
|
||||
@interface ViewController () <ASTableDataSource, ASTableDelegate>
|
||||
|
||||
@property (nonatomic, copy) NSArray *imageCategories;
|
||||
@property (nonatomic, strong, readonly) ASTableNode *tableNode;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
@@ -77,12 +80,12 @@
|
||||
|
||||
#pragma mark - ASTableDataSource / ASTableDelegate
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return self.imageCategories.count;
|
||||
}
|
||||
|
||||
- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSString *imageCategory = self.imageCategories[indexPath.row];
|
||||
return ^{
|
||||
@@ -92,15 +95,13 @@
|
||||
};
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSString *imageCategory = self.imageCategories[indexPath.row];
|
||||
DetailRootNode *detailRootNode = [[DetailRootNode alloc] initWithImageCategory:imageCategory];
|
||||
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNode:detailRootNode];
|
||||
detailViewController.title = [imageCategory capitalizedString];
|
||||
[self.navigationController pushViewController:detailViewController animated:YES];
|
||||
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
@interface ViewController : ASViewController
|
||||
|
||||
@end
|
||||
|
||||
@@ -29,9 +29,9 @@ static const NSInteger kBatchSize = 20;
|
||||
static const CGFloat kHorizontalSectionPadding = 10.0f;
|
||||
static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
|
||||
@interface ViewController () <ASCollectionViewDataSource, ASCollectionViewDelegateFlowLayout>
|
||||
@interface ViewController () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout>
|
||||
{
|
||||
ASCollectionView *_collectionView;
|
||||
ASCollectionNode *_collectionNode;
|
||||
NSMutableArray *_data;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
self = [super initWithNode:_collectionNode];
|
||||
|
||||
if (self) {
|
||||
|
||||
@@ -53,24 +53,23 @@ static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
|
||||
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
|
||||
|
||||
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
_collectionView.asyncDataSource = self;
|
||||
_collectionView.asyncDelegate = self;
|
||||
_collectionView.backgroundColor = [UIColor grayColor];
|
||||
_collectionView.leadingScreensForBatching = 2;
|
||||
_collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout];
|
||||
_collectionNode.dataSource = self;
|
||||
_collectionNode.delegate = self;
|
||||
_collectionNode.backgroundColor = [UIColor grayColor];
|
||||
|
||||
ASRangeTuningParameters preloadTuning;
|
||||
preloadTuning.leadingBufferScreenfuls = 2;
|
||||
preloadTuning.trailingBufferScreenfuls = 1;
|
||||
[_collectionView setTuningParameters:preloadTuning forRangeType:ASLayoutRangeTypePreload];
|
||||
[_collectionNode setTuningParameters:preloadTuning forRangeType:ASLayoutRangeTypePreload];
|
||||
|
||||
ASRangeTuningParameters preRenderTuning;
|
||||
preRenderTuning.leadingBufferScreenfuls = 1;
|
||||
preRenderTuning.trailingBufferScreenfuls = 0.5;
|
||||
[_collectionView setTuningParameters:preRenderTuning forRangeType:ASLayoutRangeTypeDisplay];
|
||||
[_collectionNode setTuningParameters:preRenderTuning forRangeType:ASLayoutRangeTypeDisplay];
|
||||
|
||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||
[_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
|
||||
|
||||
_data = [[NSMutableArray alloc] init];
|
||||
|
||||
@@ -85,7 +84,8 @@ static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.view addSubview:_collectionView];
|
||||
// set any collectionView properties here (once the node's backing view is loaded)
|
||||
_collectionNode.view.leadingScreensForBatching = 2;
|
||||
[self fetchMoreCatsWithCompletion:nil];
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
- (void)appendMoreItems:(NSInteger)numberOfNewItems completion:(void (^)(BOOL))completion {
|
||||
NSArray *newData = [self getMoreData:numberOfNewItems];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_collectionView performBatchUpdates:^{
|
||||
[_collectionNode performBatchAnimated:YES updates:^{
|
||||
[_data addObjectsFromArray:newData];
|
||||
NSArray *addedIndexPaths = [self indexPathsForObjects:newData];
|
||||
[_collectionView insertItemsAtIndexPaths:addedIndexPaths];
|
||||
[_collectionNode insertItemsAtIndexPaths:addedIndexPaths];
|
||||
} completion:completion];
|
||||
});
|
||||
}
|
||||
@@ -142,19 +142,13 @@ static const CGFloat kVerticalSectionPadding = 20.0f;
|
||||
return indexPaths;
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
_collectionView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
||||
[_collectionView.collectionViewLayout invalidateLayout];
|
||||
[_collectionNode.view.collectionViewLayout invalidateLayout];
|
||||
}
|
||||
|
||||
- (void)reloadTapped
|
||||
{
|
||||
[_collectionView reloadData];
|
||||
[_collectionNode reloadData];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@protocol MosaicCollectionViewLayoutDelegate <ASCollectionViewDelegate>
|
||||
@protocol MosaicCollectionViewLayoutDelegate <ASCollectionDelegate>
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(MosaicCollectionViewLayout *)layout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
@@ -38,4 +38,4 @@
|
||||
|
||||
@interface MosaicCollectionViewLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -214,18 +214,6 @@
|
||||
return ASSizeRangeMake(CGSizeZero, [layout _headerSizeForSection:indexPath.section]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the inspector for the number of supplementary sections in the collection view for the given kind.
|
||||
*/
|
||||
- (NSUInteger)collectionView:(ASCollectionView *)collectionView numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind
|
||||
{
|
||||
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
|
||||
return [[collectionView asyncDataSource] numberOfSectionsInCollectionView:collectionView];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the inspector for the number of supplementary views for the given kind in the specified section.
|
||||
*/
|
||||
@@ -238,4 +226,4 @@
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
@interface ViewController : ASViewController
|
||||
|
||||
@end
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
|
||||
static NSUInteger kNumberOfImages = 14;
|
||||
|
||||
@interface ViewController () <ASCollectionViewDataSource, MosaicCollectionViewLayoutDelegate>
|
||||
@interface ViewController () <ASCollectionDataSource, ASCollectionDelegate>
|
||||
{
|
||||
NSMutableArray *_sections;
|
||||
ASCollectionView *_collectionView;
|
||||
ASCollectionNode *_collectionNode;
|
||||
MosaicCollectionViewLayoutInspector *_layoutInspector;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ static NSUInteger kNumberOfImages = 14;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
if (!(self = [super initWithNode:_collectionNode]))
|
||||
return nil;
|
||||
|
||||
_sections = [NSMutableArray array];
|
||||
@@ -59,44 +59,37 @@ static NSUInteger kNumberOfImages = 14;
|
||||
|
||||
_layoutInspector = [[MosaicCollectionViewLayoutInspector alloc] init];
|
||||
|
||||
_collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
|
||||
_collectionView.asyncDataSource = self;
|
||||
_collectionView.asyncDelegate = self;
|
||||
_collectionView.layoutInspector = _layoutInspector;
|
||||
_collectionView.backgroundColor = [UIColor whiteColor];
|
||||
_collectionNode.dataSource = self;
|
||||
_collectionNode.delegate = self;
|
||||
_collectionNode.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
[_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_collectionView.asyncDataSource = nil;
|
||||
_collectionView.asyncDelegate = nil;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.view addSubview:_collectionView];
|
||||
_collectionNode.view.layoutInspector = _layoutInspector;
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
- (void)dealloc
|
||||
{
|
||||
_collectionView.frame = self.view.bounds;
|
||||
_collectionNode.dataSource = nil;
|
||||
_collectionNode.delegate = nil;
|
||||
}
|
||||
|
||||
- (void)reloadTapped
|
||||
{
|
||||
[_collectionView reloadData];
|
||||
[_collectionNode reloadData];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASCollectionView data source.
|
||||
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
UIImage *image = _sections[indexPath.section][indexPath.item];
|
||||
return ^{
|
||||
@@ -105,7 +98,7 @@ static NSUInteger kNumberOfImages = 14;
|
||||
}
|
||||
|
||||
|
||||
- (ASCellNode *)collectionView:(ASCollectionView *)collectionView nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSDictionary *textAttributes = @{
|
||||
NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline],
|
||||
@@ -117,17 +110,17 @@ static NSUInteger kNumberOfImages = 14;
|
||||
return textCellNode;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(ASCollectionNode *)collectionNode
|
||||
{
|
||||
return _sections.count;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
- (NSInteger)collectionView:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return [_sections[section] count];
|
||||
}
|
||||
|
||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath
|
||||
- (CGSize)collectionView:(ASCollectionNode *)collectionNode layout:(UICollectionViewLayout *)collectionViewLayout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return [(UIImage *)_sections[indexPath.section][indexPath.item] size];
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* This ASCellNode contains an ASCollectionNode. It intelligently interacts with a containing ASCollectionView or ASTableView,
|
||||
* to preload and clean up contents as the user scrolls around both vertically and horizontally — in a way that minimizes memory usage.
|
||||
*/
|
||||
@interface HorizontalScrollCellNode : ASCellNode <ASCollectionViewDelegate, ASCollectionViewDataSource>
|
||||
@interface HorizontalScrollCellNode : ASCellNode <ASCollectionDelegate, ASCollectionDataSource>
|
||||
|
||||
- (instancetype)initWithElementSize:(CGSize)size;
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ViewController : UIViewController
|
||||
@interface ViewController : ASViewController
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,15 +15,14 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
|
||||
#import "ViewController.h"
|
||||
#import "HorizontalScrollCellNode.h"
|
||||
|
||||
@interface ViewController () <ASTableViewDataSource, ASTableViewDelegate>
|
||||
@interface ViewController () <ASTableDataSource, ASTableDelegate>
|
||||
{
|
||||
ASTableView *_tableView;
|
||||
ASTableNode *_tableNode;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -35,13 +34,12 @@
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
if (!(self = [super initWithNode:_tableNode]))
|
||||
return nil;
|
||||
|
||||
_tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
|
||||
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
_tableView.asyncDataSource = self;
|
||||
_tableView.asyncDelegate = self;
|
||||
_tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
|
||||
_tableNode.dataSource = self;
|
||||
_tableNode.delegate = self;
|
||||
|
||||
self.title = @"Horizontal Scrolling Gradients";
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRedo
|
||||
@@ -51,21 +49,16 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)reloadEverything
|
||||
{
|
||||
[_tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.view addSubview:_tableView];
|
||||
_tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
- (void)reloadEverything
|
||||
{
|
||||
_tableView.frame = self.view.bounds;
|
||||
[_tableNode reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - ASTableView.
|
||||
|
||||
Reference in New Issue
Block a user