Files
Swiftgram/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m
Adlai Holler c2ea7cfeac [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
2016-10-14 17:21:16 -07:00

234 lines
9.3 KiB
Objective-C

//
// ASCollectionViewFlowLayoutInspector.m
// AsyncDisplayKit
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASCollectionViewFlowLayoutInspector.h"
#import "ASCollectionView.h"
#import "ASAssert.h"
#import "ASEqualityHelpers.h"
#import "ASCollectionView+Undeprecated.h"
#define kDefaultItemSize CGSizeMake(50, 50)
#pragma mark - Helper Functions
// Returns a constrained size to let the cells layout itself as far as possible based on the scrollable direction
// of the collection view
static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView) {
CGSize maxSize = collectionView.bounds.size;
if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) {
maxSize.width = CGFLOAT_MAX;
} else {
maxSize.height = CGFLOAT_MAX;
}
return ASSizeRangeMake(CGSizeZero, maxSize);
}
#pragma mark - ASCollectionViewLayoutInspector
@implementation ASCollectionViewLayoutInspector {
struct {
unsigned int implementsConstrainedSizeForNodeAtIndexPath:1;
} _delegateFlags;
}
#pragma mark Lifecycle
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
self = [super init];
if (self != nil) {
[self didChangeCollectionViewDelegate:collectionView.asyncDelegate];
}
return self;
}
#pragma mark ASCollectionViewLayoutInspecting
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate
{
if (delegate == nil) {
memset(&_delegateFlags, 0, sizeof(_delegateFlags));
} else {
_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];
}
}
- (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.");
}
return NodeConstrainedSizeForScrollDirection(collectionView);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return ASSizeRangeMake(CGSizeZero, CGSizeZero);
}
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return 0;
}
@end
#pragma mark - ASCollectionViewFlowLayoutInspector
@interface ASCollectionViewFlowLayoutInspector ()
@property (nonatomic, weak) UICollectionViewFlowLayout *layout;
@end
@implementation ASCollectionViewFlowLayoutInspector {
struct {
unsigned int implementsReferenceSizeForHeader:1;
unsigned int implementsReferenceSizeForFooter:1;
unsigned int implementsConstrainedSizeForNodeAtIndexPath:1;
} _delegateFlags;
struct {
unsigned int implementsNumberOfSectionsInCollectionView:1;
} _dataSourceFlags;
}
#pragma mark Lifecycle
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout;
{
NSParameterAssert(collectionView);
NSParameterAssert(flowLayout);
self = [super init];
if (self != nil) {
[self didChangeCollectionViewDataSource:collectionView.asyncDataSource];
[self didChangeCollectionViewDelegate:collectionView.asyncDelegate];
_layout = flowLayout;
}
return self;
}
#pragma mark ASCollectionViewLayoutInspecting
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
{
if (delegate == nil) {
memset(&_delegateFlags, 0, sizeof(_delegateFlags));
} else {
_delegateFlags.implementsReferenceSizeForHeader = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForHeaderInSection:)];
_delegateFlags.implementsReferenceSizeForFooter = [delegate respondsToSelector:@selector(collectionView:layout:referenceSizeForFooterInSection:)];
_delegateFlags.implementsConstrainedSizeForNodeAtIndexPath = [delegate respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];
}
}
- (void)didChangeCollectionViewDataSource:(id<ASCollectionDataSource>)dataSource
{
if (dataSource == nil) {
memset(&_dataSourceFlags, 0, sizeof(_dataSourceFlags));
} else {
_dataSourceFlags.implementsNumberOfSectionsInCollectionView = [dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)];
}
}
- (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.");
}
CGSize itemSize = _layout.itemSize;
if (CGSizeEqualToSize(itemSize, kDefaultItemSize) == NO) {
return ASSizeRangeMake(itemSize, itemSize);
}
return NodeConstrainedSizeForScrollDirection(collectionView);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
CGSize constrainedSize;
CGSize supplementarySize = [self sizeForSupplementaryViewOfKind:kind inSection:indexPath.section collectionView:collectionView];
if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) {
constrainedSize = CGSizeMake(CGRectGetWidth(collectionView.bounds), supplementarySize.height);
} else {
constrainedSize = CGSizeMake(supplementarySize.width, CGRectGetHeight(collectionView.bounds));
}
return ASSizeRangeMake(CGSizeZero, constrainedSize);
}
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
return [self layoutHasSupplementaryViewOfKind:kind inSection:section collectionView:collectionView] ? 1 : 0;
}
- (ASScrollDirection)scrollableDirections
{
return (self.layout.scrollDirection == UICollectionViewScrollDirectionHorizontal) ? ASScrollDirectionHorizontalDirections : ASScrollDirectionVerticalDirections;
}
#pragma mark - Private helpers
- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView
{
if (ASObjectIsEqual(kind, UICollectionElementKindSectionHeader)) {
if (_delegateFlags.implementsReferenceSizeForHeader) {
return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForHeaderInSection:section];
} else {
return [self.layout headerReferenceSize];
}
} else if (ASObjectIsEqual(kind, UICollectionElementKindSectionFooter)) {
if (_delegateFlags.implementsReferenceSizeForFooter) {
return [[self delegateForCollectionView:collectionView] collectionView:collectionView layout:_layout referenceSizeForFooterInSection:section];
} else {
return [self.layout footerReferenceSize];
}
} else {
return CGSizeZero;
}
}
- (BOOL)layoutHasSupplementaryViewOfKind:(NSString *)kind inSection:(NSUInteger)section collectionView:(ASCollectionView *)collectionView
{
CGSize size = [self sizeForSupplementaryViewOfKind:kind inSection:section collectionView:collectionView];
return [self usedLayoutValueForSize:size] > 0;
}
- (CGFloat)usedLayoutValueForSize:(CGSize)size
{
if (_layout.scrollDirection == UICollectionViewScrollDirectionVertical) {
return size.height;
} else {
return size.width;
}
}
- (id<ASCollectionViewDelegateFlowLayout>)delegateForCollectionView:(ASCollectionView *)collectionView
{
return (id<ASCollectionViewDelegateFlowLayout>)collectionView.asyncDelegate;
}
@end