Merge pull request #762 from levi/supplementary-views

Respond to supplementary node feedback
This commit is contained in:
appleguy 2015-10-25 19:45:00 -07:00
commit 7d013a70ac
8 changed files with 50 additions and 46 deletions

View File

@ -90,7 +90,7 @@
* collection view layout subclasses will need to provide their own implementation of an inspector object for their
* supplementary views to be compatible with `ASCollectionView`'s supplementary node support.
*/
@property (nonatomic, weak) id<ASCollectionViewLayoutInspecting> layoutDelegate;
@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.

View File

@ -153,7 +153,7 @@ static BOOL _isInterceptedSelector(SEL sel)
CGSize _maxSizeForNodesConstrainedSize;
BOOL _ignoreMaxSizeChange;
NSMutableArray *_registeredSupplementaryKinds;
NSMutableSet *_registeredSupplementaryKinds;
/**
* If YES, the `UICollectionView` will reload its data on next layout pass so we should not forward any updates to it.
@ -229,10 +229,10 @@ static BOOL _isInterceptedSelector(SEL sel)
// Register the default layout inspector delegate for flow layouts only, custom layouts
// will need to roll their own ASCollectionViewLayoutInspecting implementation and set a layout delegate
if ([layout asdk_isFlowLayout]) {
_layoutDelegate = [self flowLayoutInspector];
_layoutInspector = [self flowLayoutInspector];
}
_registeredSupplementaryKinds = [NSMutableArray array];
_registeredSupplementaryKinds = [NSMutableSet set];
self.backgroundColor = [UIColor whiteColor];
@ -256,6 +256,7 @@ static BOOL _isInterceptedSelector(SEL sel)
{
if (_flowLayoutInspector == nil) {
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
ASDisplayNodeAssertNotNil(layout, @"Collection view layout must be a flow layout to use the built-in inspector");
_flowLayoutInspector = [[ASCollectionViewFlowLayoutInspector alloc] initWithCollectionView:self
flowLayout:layout];
}
@ -333,16 +334,7 @@ static BOOL _isInterceptedSelector(SEL sel)
_asyncDelegateImplementsInsetSection = ([_asyncDelegate respondsToSelector:@selector(collectionView:layout:insetForSectionAtIndex:)] ? 1 : 0);
}
[_flowLayoutInspector cacheSelectorsForCollectionView:self];
}
- (void)setCollectionViewLayout:(UICollectionViewLayout *)collectionViewLayout
{
[super setCollectionViewLayout:collectionViewLayout];
if ([collectionViewLayout asdk_isFlowLayout]) {
_flowLayoutInspector = nil;
_layoutDelegate = [self flowLayoutInspector];
}
[_layoutInspector didChangeCollectionViewDelegate:asyncDelegate];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
@ -745,25 +737,25 @@ static BOOL _isInterceptedSelector(SEL sel)
- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController
{
return _registeredSupplementaryKinds;
return [_registeredSupplementaryKinds allObjects];
}
- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutDelegate collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath];
}
- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutDelegate collectionView:self supplementaryNodesOfKind:kind inSection:section];
ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section];
}
- (NSUInteger)dataController:(ASCollectionDataController *)dataController numberOfSectionsForSupplementaryNodeOfKind:(NSString *)kind;
{
ASDisplayNodeAssert(_layoutDelegate != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutDelegate collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind];
ASDisplayNodeAssert(_layoutInspector != nil, @"To support supplementary nodes in ASCollectionView, it must have a layoutDelegate for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return [_layoutInspector collectionView:self numberOfSectionsForSupplementaryNodeOfKind:kind];
}
#pragma mark - ASRangeControllerDelegate.

View File

@ -61,7 +61,7 @@
NSUInteger sectionCount = [self.collectionDataSource dataController:self numberOfSectionsForSupplementaryNodeOfKind:kind];
NSMutableArray *sections = [NSMutableArray arrayWithCapacity:sectionCount];
for (int i = 0; i < sectionCount; i++) {
[sections addObject:[[NSMutableArray alloc] init]];
[sections addObject:[NSMutableArray array]];
}
[self insertSections:sections ofKind:kind atIndexSet:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)] completion:nil];

View File

@ -11,6 +11,7 @@
#import <AsyncDisplayKit/ASDimension.h>
@class ASCollectionView;
@protocol ASCollectionViewDelegate;
@protocol ASCollectionViewLayoutInspecting <NSObject>
@ -29,6 +30,13 @@
*/
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section;
/**
* Allow the inspector to respond to delegate changes.
*
* @discussion A great time to update perform selector caches!
*/
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate;
@end
@interface ASCollectionViewFlowLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
@ -37,6 +45,4 @@
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout;
- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView;
@end

View File

@ -11,6 +11,8 @@
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASCollectionView.h"
#import "ASAssert.h"
#import "ASEqualityHelpers.h"
@implementation ASCollectionViewFlowLayoutInspector {
BOOL _delegateImplementsReferenceSizeForHeader;
@ -24,24 +26,24 @@
self = [super init];
if (flowLayout == nil) {
return nil;
ASDisplayNodeAssert(NO, @"Should never create a layout inspector without a layout");
}
if (self != nil) {
[self cacheSelectorsForCollectionView:collectionView];
[self didChangeCollectionViewDelegate:collectionView.asyncDelegate];
_layout = flowLayout;
}
return self;
}
- (void)cacheSelectorsForCollectionView:(ASCollectionView *)collectionView
- (void)didChangeCollectionViewDelegate:(id<ASCollectionViewDelegate>)delegate;
{
if (collectionView == nil) {
_delegateImplementsReferenceSizeForHeader = nil;
_delegateImplementsReferenceSizeForFooter = nil;
if (delegate == nil) {
_delegateImplementsReferenceSizeForHeader = NO;
_delegateImplementsReferenceSizeForFooter = NO;
} else {
_delegateImplementsReferenceSizeForHeader = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)];
_delegateImplementsReferenceSizeForFooter = [[self delegateForCollectionView:collectionView] respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)];
_delegateImplementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)];
_delegateImplementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)];
}
}
@ -77,13 +79,13 @@
- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView
{
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
if (ASObjectIsEqual(kind, UICollectionElementKindSectionHeader)) {
if (_delegateImplementsReferenceSizeForHeader) {
return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:section];
} else {
return [self.layout headerReferenceSize];
}
} else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
} else if (ASObjectIsEqual(kind, UICollectionElementKindSectionFooter)) {
if (_delegateImplementsReferenceSizeForFooter) {
return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:section];
} else {

View File

@ -74,8 +74,8 @@
/**
* Notifies the subclass to perform any work needed before the data controller is reloaded entirely
*
* @discussion This method will be performed before the data controller enters its editing queue, usually on the main
* thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
* @discussion This method will be performed before the data controller enters its editing queue.
* The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
* data stores before entering into editing the backing store on a background thread.
*/
- (void)prepareForReloadData;
@ -92,8 +92,8 @@
/**
* Notifies the subclass to perform setup before sections are inserted in the data controller
*
* @discussion This method will be performed before the data controller enters its editing queue, usually on the main
* thread. The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
* @discussion This method will be performed before the data controller enters its editing queue.
* The data source is locked at this point and accessing it is safe. Use this method to set up any nodes or
* data stores before entering into editing the backing store on a background thread.
*
* @param sections Indices of sections to be inserted

View File

@ -15,6 +15,7 @@
#import "ASDisplayNode.h"
#import "ASMultidimensionalArrayUtils.h"
#import "ASDisplayNodeInternal.h"
#import "ASLayout.h"
//#define LOG(...) NSLog(__VA_ARGS__)
#define LOG(...)
@ -185,8 +186,9 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
dispatch_group_wait(layoutGroup, DISPATCH_TIME_FOREVER);
free(nodeBoundSizes);
if (completionBlock)
if (completionBlock) {
completionBlock(nodes, indexPaths);
}
}
- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
@ -218,8 +220,10 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
{
if (indexPaths.count == 0)
if (indexPaths.count == 0) {
return;
}
LOG(@"_deleteNodesAtIndexPaths:%@ ofKind:%@, full index paths in _editingNodes = %@", indexPaths, kind, ASIndexPathsForMultidimensionalArray(_editingNodes[kind]));
NSMutableArray *editingNodes = _editingNodes[kind];
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
@ -818,8 +822,8 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
[section enumerateObjectsUsingBlock:^(ASCellNode *node, NSUInteger rowIndex, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:sectionIndex];
ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath];
[node measureWithSizeRange:constrainedSize];
node.frame = CGRectMake(0.0f, 0.0f, node.calculatedSize.width, node.calculatedSize.height);
ASLayout *layout = [node measureWithSizeRange:constrainedSize];
node.frame = CGRectMake(0.0f, 0.0f, layout.size.width, layout.size.height);
}];
}];
}];
@ -849,7 +853,7 @@ static void *kASSizingQueueContext = &kASSizingQueueContext;
- (NSArray *)indexPathsForEditingNodesOfKind:(NSString *)kind
{
return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : [NSArray array];
return _editingNodes[kind] != nil ? ASIndexPathsForMultidimensionalArray(_editingNodes[kind]) : nil;
}
- (NSMutableArray *)editingNodesOfKind:(NSString *)kind

View File

@ -91,15 +91,15 @@
{
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
XCTAssert(collectionView.layoutDelegate != nil, @"should automatically set a layout delegate for flow layouts");
XCTAssert([collectionView.layoutDelegate isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default");
XCTAssert(collectionView.layoutInspector != nil, @"should automatically set a layout delegate for flow layouts");
XCTAssert([collectionView.layoutInspector isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default");
}
- (void)testThatItDoesNotSetALayoutInspectorForCustomLayouts
{
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
XCTAssert(collectionView.layoutDelegate == nil, @"should not set a layout delegate for custom layouts");
XCTAssert(collectionView.layoutInspector == nil, @"should not set a layout delegate for custom layouts");
}
- (void)testThatRegisteringASupplementaryNodeStoresItForIntrospection