Add default ASCollectionViewLayoutInspecting for custom ASCollectionViewLayout

This commit is contained in:
Michael Schneider
2016-06-17 13:26:05 -07:00
parent 35c860c183
commit 9fb3129a0e
4 changed files with 64 additions and 27 deletions

View File

@@ -254,7 +254,7 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
// Custom layouts will need to roll their own ASCollectionViewLayoutInspecting implementation and set a layout
// delegate. In the meantime ASDK provides a null layout inspector that does not provide any implementation
// and throws an exception for methods that should be implemented in the <ASCollectionViewLayoutInspecting>
_defaultLayoutInspector = [[ASCollectionViewNullLayoutInspector alloc] init];
_defaultLayoutInspector = [[ASCollectionViewDefaultCustomLayoutInspector alloc] initWithCollectionView:self];
}
_layoutInspector = _defaultLayoutInspector;

View File

@@ -60,10 +60,14 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
* Simple "Null Object" inspector for non-flow layouts that does throws exceptions if methods are called
* from <ASCollectionViewLayoutInspecting>
* A layout inspector for non-flow layouts that returns a constrained size to let the cells layout itself as
* far as possible based on the scrollable direction of the collection view. It throws exceptions for delegate
* methods that are related to supplementary node's management.
*/
@interface ASCollectionViewNullLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
@interface ASCollectionViewDefaultCustomLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView NS_DESIGNATED_INITIALIZER;
@end
@@ -74,7 +78,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, weak, readonly) UICollectionViewFlowLayout *layout;
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout NS_DESIGNATED_INITIALIZER;
@end

View File

@@ -15,14 +15,57 @@
#define kDefaultItemSize CGSizeMake(50, 50)
#pragma mark - ASCollectionViewNullLayoutInspector
#pragma mark - Helper Functions
@implementation ASCollectionViewNullLayoutInspector
// Returns a constrained size to let the cells layout itself as far as possible based on the scrollable direction
// of the collection view
static ASSizeRange ASDefaultConstrainedSizeForNodeForCollectionView(ASCollectionView *collectionView) {
CGSize maxSize = collectionView.bounds.size;
if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) {
maxSize.width = FLT_MAX;
} else {
maxSize.height = FLT_MAX;
}
return ASSizeRangeMake(CGSizeZero, maxSize);
}
#pragma mark - ASCollectionViewDefaultCustomLayoutInspector
@implementation ASCollectionViewDefaultCustomLayoutInspector {
struct {
unsigned int implementsConstrainedSizeForNodeAtIndexPath:1;
} _dataSourceFlags;
}
#pragma mark Lifecycle
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
self = [super init];
if (self != nil) {
[self didChangeCollectionViewDataSource:collectionView.asyncDataSource];
}
return self;
}
#pragma mark ASCollectionViewLayoutInspecting
- (void)didChangeCollectionViewDataSource:(id<ASCollectionDataSource>)dataSource
{
if (dataSource == nil) {
memset(&_dataSourceFlags, 0, sizeof(_dataSourceFlags));
} else {
_dataSourceFlags.implementsConstrainedSizeForNodeAtIndexPath = [dataSource respondsToSelector:@selector(collectionView:constrainedSizeForNodeAtIndexPath:)];
}
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
ASDisplayNodeAssert(NO, @"To support a custom collection view layout in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)");
return ASSizeRangeMake(CGSizeZero, CGSizeZero);
if (_dataSourceFlags.implementsConstrainedSizeForNodeAtIndexPath) {
return [collectionView.asyncDataSource collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath];
}
return ASDefaultConstrainedSizeForNodeForCollectionView(collectionView);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
@@ -64,7 +107,7 @@
} _dataSourceFlags;
}
#pragma mark - Accessors
#pragma mark Lifecycle
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView flowLayout:(UICollectionViewFlowLayout *)flowLayout;
{
@@ -80,6 +123,8 @@
return self;
}
#pragma mark ASCollectionViewLayoutInspecting
- (void)didChangeCollectionViewDelegate:(id<ASCollectionDelegate>)delegate;
{
if (delegate == nil) {
@@ -100,29 +145,18 @@
}
}
#pragma mark - ASCollectionViewLayoutInspecting
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
// First check if delegate provides a constrained size
if (_dataSourceFlags.implementsConstrainedSizeForNodeAtIndexPath) {
return [collectionView.asyncDataSource collectionView:collectionView constrainedSizeForNodeAtIndexPath:indexPath];
}
// Check if item size as constrained size is given
CGSize itemSize = _layout.itemSize;
if (!CGSizeEqualToSize(itemSize, kDefaultItemSize)) {
return ASSizeRangeMake(itemSize, itemSize);
}
// No constrained size is given try to let the cells layout itself as far as possible based on the scrollable direction
CGSize maxSize = collectionView.bounds.size;
if (ASScrollDirectionContainsHorizontalDirection([collectionView scrollableDirections])) {
maxSize.width = FLT_MAX;
} else {
maxSize.height = FLT_MAX;
}
return ASSizeRangeMake(CGSizeZero, maxSize);
return ASDefaultConstrainedSizeForNodeForCollectionView(collectionView);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath

View File

@@ -124,14 +124,12 @@
XCTAssert([collectionView.layoutInspector isKindOfClass:[ASCollectionViewFlowLayoutInspector class]], @"should have a flow layout inspector by default");
}
- (void)testThatItDoesNotSetALayoutInspectorForCustomLayouts
- (void)testThatISetALayoutInspectorForCustomLayouts
{
UICollectionViewLayout *layout = [[UICollectionViewLayout alloc] init];
ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
XCTAssert(collectionView.layoutInspector != nil, @"should automatically set a layout delegate for custom layouts");
XCTAssert([collectionView.layoutInspector isKindOfClass:[ASCollectionViewNullLayoutInspector class]], @"should have a null layout inspector by default if no layout inspector is given for a custom layout");
XCTAssertThrows([collectionView.layoutInspector collectionView:collectionView constrainedSizeForNodeAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]], @"should throw an exception for <ASCollectionViewLayoutInspecting> methods");
XCTAssertThrows([collectionView.layoutInspector collectionView:collectionView supplementaryNodesOfKind:UICollectionElementKindSectionHeader inSection:0], @"should throw an exception for <ASCollectionViewLayoutInspecting> methods");
XCTAssert(collectionView.layoutInspector != nil, @"should automatically set a layout delegate for flow layouts");
XCTAssert([collectionView.layoutInspector isKindOfClass:[ASCollectionViewDefaultCustomLayoutInspector class]], @"should have a flow layout inspector by default");
}
- (void)testThatRegisteringASupplementaryNodeStoresItForIntrospection