Further implement data controller support and layout introspection

This commit is contained in:
Levi McCallum
2015-09-29 08:59:40 -07:00
committed by Levi McCallum
parent 658b78d552
commit da7a2a5d48
9 changed files with 121 additions and 20 deletions

View File

@@ -17,7 +17,7 @@
@class ASCellNode;
@protocol ASCollectionViewDataSource;
@protocol ASCollectionViewDelegate;
@protocol ASCollectionViewLayoutInspecting;
/**
* Node-based collection view.
@@ -80,6 +80,13 @@
*/
@property (nonatomic, assign) CGFloat leadingScreensForBatching;
/**
* Optional introspection object for the collection view's layout.
*
* TODO: Discuss more about this delegate
*/
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutDelegate;
/**
* 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.
@@ -119,7 +126,7 @@
*/
- (void)reloadData;
- (void)registerSupplementaryViewOfKind:(NSString *)elementKind;
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind;
/**
* Inserts one or more sections.

View File

@@ -16,6 +16,7 @@
#import "ASBatchFetching.h"
#import "UICollectionViewLayout+ASConvenience.h"
#import "ASInternalHelpers.h"
#import "ASCollectionViewFlowLayoutInspector.h"
// FIXME: Temporary nonsense import until method names are finalized and exposed
#import "ASDisplayNode+Subclasses.h"
@@ -137,6 +138,7 @@ static BOOL _isInterceptedSelector(SEL sel)
ASCollectionDataController *_dataController;
ASRangeController *_rangeController;
ASCollectionViewLayoutController *_layoutController;
ASCollectionViewFlowLayoutInspector *_flowLayoutInspector;
BOOL _performingBatchUpdates;
NSMutableArray *_batchUpdateBlocks;
@@ -203,6 +205,10 @@ static BOOL _isInterceptedSelector(SEL sel)
_dataController.delegate = _rangeController;
_dataController.dataSource = self;
_flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] init];
// TODO: Implement a better path of falling-back to a flow layout
_flowLayoutInspector.layout = (UICollectionViewFlowLayout *)layout;
_batchContext = [[ASBatchContext alloc] init];
_leadingScreensForBatching = 1.0;
@@ -377,7 +383,7 @@ static BOOL _isInterceptedSelector(SEL sel)
[self performBatchAnimated:YES updates:updates completion:completion];
}
- (void)registerSupplementaryViewOfKind:(NSString *)elementKind
- (void)registerSupplementaryNodeOfKind:(NSString *)elementKind
{
[self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:elementKind
withReuseIdentifier:[self __reuseIdentifierForKind:elementKind]];
@@ -677,6 +683,11 @@ static BOOL _isInterceptedSelector(SEL sel)
return constrainedSize;
}
- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
return [self.layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
}
- (NSUInteger)dataController:(ASDataController *)dataController rowsInSection:(NSUInteger)section
{
return [_asyncDataSource collectionView:self numberOfItemsInSection:section];

View File

@@ -17,9 +17,14 @@
@protocol ASCollectionDataControllerSource <ASDataControllerSource>
- (ASDisplayNode *)dataController:(ASDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (ASDisplayNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (NSArray *)supplementaryKindsInDataController:(ASCollectionDataController *)dataController;
/**
The constrained size range for layout.
*/
- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController;
- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryKind:(NSString *)kind;

View File

@@ -14,6 +14,12 @@
#import "ASDisplayNodeInternal.h"
#import "ASDataController+Subclasses.h"
@interface ASCollectionDataController ()
- (id<ASCollectionDataControllerSource>)collectionDataSource;
@end
@implementation ASCollectionDataController {
NSMutableDictionary *_completedSupplementaryNodes;
NSMutableDictionary *_editingSupplementaryNodes;
@@ -24,21 +30,23 @@
[self performEditCommandWithBlock:^{
ASDisplayNodeAssertMainThread();
[self accessDataSourceWithBlock:^{
NSArray *elementKinds = [self.collectionDataSource supplementaryKindsInDataController:self];
NSArray *elementKinds = [self.collectionDataSource supplementaryNodeKindsInDataController:self];
[elementKinds enumerateObjectsUsingBlock:^(NSString *kind, NSUInteger idx, BOOL * _Nonnull stop) {
_completedSupplementaryNodes[kind] = [NSMutableArray array];
_editingSupplementaryNodes[kind] = [NSMutableArray array];
NSMutableArray *indexPaths = [NSMutableArray array];
NSMutableArray *nodes = [NSMutableArray array];
[self _populateAllNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths];
[self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:nil];
[self _populateSupplementaryNodesOfKind:kind withMutableNodes:nodes mutableIndexPaths:indexPaths];
[self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) {
return [self.collectionDataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
} completion:nil];
}];
}];
}];
}
- (void)_populateAllNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths
- (void)_populateSupplementaryNodesOfKind:(NSString *)kind withMutableNodes:(NSMutableArray *)nodes mutableIndexPaths:(NSMutableArray *)indexPaths
{
NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryKind:kind];
for (NSUInteger i = 0; i < sectionCount; i++) {
@@ -65,6 +73,7 @@
#pragma mark - Internal Data Querying
// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController
- (void)_insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths
{
if (indexPaths.count == 0)
@@ -78,6 +87,7 @@
});
}
// TODO: Reduce code duplication by exposing generic insert/delete helpers from ASDataController
- (void)_deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths
{
if (indexPaths.count == 0)

View File

@@ -0,0 +1,31 @@
//
// ASCollectionViewFlowLayoutInspector.h
// Pods
//
// Created by Levi McCallum on 9/29/15.
//
//
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASDimension.h>
@class ASCollectionView;
@protocol ASCollectionViewLayoutInspecting <NSObject>
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section;
@end
@interface ASCollectionViewFlowLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
@property (nonatomic, weak) UICollectionViewFlowLayout *layout;
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section;
@end

View File

@@ -0,0 +1,33 @@
//
// ASCollectionViewFlowLayoutInspector.m
// Pods
//
// Created by Levi McCallum on 9/29/15.
//
//
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASCollectionView.h"
@implementation ASCollectionViewFlowLayoutInspector
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
// TODO: Implement some heuristic that follows the width/height constraints of header and footer supplementary views
return ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX, FLT_MAX));
}
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryViewsOfKind:(NSString *)kind inSection:(NSUInteger)section
{
NSUInteger count = 0;
if (self.layout.headerReferenceSize.width > 0 || self.layout.headerReferenceSize.height > 0) {
count++;
}
if (self.layout.footerReferenceSize.width > 0 || self.layout.footerReferenceSize.height > 0) {
count++;
}
return count;
}
@end

View File

@@ -25,6 +25,9 @@
*/
- (void)accessDataSourceWithBlock:(dispatch_block_t)block;
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block;
/**
* Measure and layout the given nodes in optimized batches, constraining each to a given size.
*/
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock;
@end

View File

@@ -113,7 +113,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
}];
}
- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *, NSArray *))block
- (void)_layoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
{
ASDisplayNodeAssert([NSOperationQueue currentQueue] == _editingTransactionQueue, @"Cell node layout must be initiated from edit transaction queue");
@@ -129,7 +129,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
for (NSUInteger k = j; k < j + batchCount; k++) {
ASCellNode *node = nodes[k];
if (!node.isNodeLoaded) {
nodeBoundSizes[k] = [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPaths[k]];
nodeBoundSizes[k] = constraintedSizeBlock(indexPaths[k]);
}
}
@@ -151,10 +151,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
free(nodeBoundSizes);
block(nodes, indexPaths);
completionBlock(nodes, indexPaths);
}
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))block
- (void)batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths constrainedSize:(ASSizeRange (^)(NSIndexPath *indexPath))constraintedSizeBlock completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
{
NSUInteger blockSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor;
@@ -164,14 +164,15 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
NSArray *batchedIndexPaths = [indexPaths subarrayWithRange:batchedRange];
NSArray *batchedNodes = [nodes subarrayWithRange:batchedRange];
[self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths completion:block];
[self _layoutNodes:batchedNodes atIndexPaths:batchedIndexPaths constrainedSize:constraintedSizeBlock completion:completionBlock];
}
}
- (void)_batchLayoutNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
[self batchLayoutNodes:nodes atIndexPaths:indexPaths completion:^(NSArray *nodes, NSArray *indexPaths) {
[self batchLayoutNodes:nodes atIndexPaths:indexPaths constrainedSize:^ASSizeRange(NSIndexPath *indexPath) {
return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath];
} completion:^(NSArray *nodes, NSArray *indexPaths) {
// Insert finished nodes into data storage
[self _insertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions];
}];

View File

@@ -39,8 +39,8 @@
_collectionView.asyncDelegate = self;
_collectionView.backgroundColor = [UIColor whiteColor];
[_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionHeader];
[_collectionView registerSupplementaryViewOfKind:UICollectionElementKindSectionFooter];
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
[_collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionFooter];
return self;
}