[ASCollectionView] Finish support for interoperability with base-class UICollectionViewCells.

This also supports supplementary nodes. It builds off of Adlai's .interop flag but makes necessary
improvements for all of the delegate methods to work in practice with heterogenous cell types.
This commit is contained in:
Scott Goodson
2017-02-04 20:38:45 -08:00
parent e2e797be6e
commit f71eba77af
13 changed files with 281 additions and 150 deletions

View File

@@ -10,6 +10,7 @@
25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */; };
25A1FA881C02FCB000193875 /* ImageCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA871C02FCB000193875 /* ImageCellNode.m */; };
576F970133B34DFD583D5CE4 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */; };
80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */; };
9BA2CEA11BB2579C00D18414 /* Launchboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */; };
AC3C4A641A11F47200143C57 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A631A11F47200143C57 /* main.m */; };
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = AC3C4A661A11F47200143C57 /* AppDelegate.m */; };
@@ -23,6 +24,8 @@
25A1FA861C02FCB000193875 /* ImageCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCellNode.h; sourceTree = "<group>"; };
25A1FA871C02FCB000193875 /* ImageCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCellNode.m; sourceTree = "<group>"; };
4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
80364CC81E3D95A90094400C /* ImageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCollectionViewCell.h; sourceTree = "<group>"; };
80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCollectionViewCell.m; sourceTree = "<group>"; };
9BA2CEA01BB2579C00D18414 /* Launchboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launchboard.storyboard; sourceTree = "<group>"; };
AC3C4A5E1A11F47200143C57 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
AC3C4A621A11F47200143C57 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -84,6 +87,8 @@
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
AC3C4A681A11F47200143C57 /* ViewController.h */,
AC3C4A691A11F47200143C57 /* ViewController.m */,
80364CC81E3D95A90094400C /* ImageCollectionViewCell.h */,
80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */,
25A1FA861C02FCB000193875 /* ImageCellNode.h */,
25A1FA871C02FCB000193875 /* ImageCellNode.m */,
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */,
@@ -120,12 +125,12 @@
isa = PBXNativeTarget;
buildConfigurationList = AC3C4A811A11F47200143C57 /* Build configuration list for PBXNativeTarget "Sample" */;
buildPhases = (
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */,
F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */,
AC3C4A5A1A11F47200143C57 /* Sources */,
AC3C4A5B1A11F47200143C57 /* Frameworks */,
AC3C4A5C1A11F47200143C57 /* Resources */,
A6902C454C7661D0D277AC62 /* Copy Pods Resources */,
3760AAE3843D6EA89A9A166B /* Embed Pods Frameworks */,
A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */,
3760AAE3843D6EA89A9A166B /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -181,14 +186,14 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3760AAE3843D6EA89A9A166B /* Embed Pods Frameworks */ = {
3760AAE3843D6EA89A9A166B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -196,14 +201,14 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A6902C454C7661D0D277AC62 /* Copy Pods Resources */ = {
A6902C454C7661D0D277AC62 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
@@ -211,19 +216,19 @@
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Sample/Pods-Sample-resources.sh\"\n";
showEnvVarsInLog = 0;
};
F868CFBB21824CC9521B6588 /* Check Pods Manifest.lock */ = {
F868CFBB21824CC9521B6588 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
@@ -237,6 +242,7 @@
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
AC3C4A641A11F47200143C57 /* main.m in Sources */,
80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */,
25A1FA881C02FCB000193875 /* ImageCellNode.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -328,6 +334,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = Sample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = 1;
@@ -340,6 +347,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = Sample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = 1;

View File

@@ -22,5 +22,6 @@
@interface ImageCellNode : ASCellNode
- (instancetype)initWithImage:(UIImage *)image;
@property (nonatomic, strong) UIImage *image;
@end

View File

@@ -39,4 +39,14 @@
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_imageNode];
}
- (void)setImage:(UIImage *)image
{
_imageNode.image = image;
}
- (UIImage *)image
{
return _imageNode.image;
}
@end

View File

@@ -0,0 +1,13 @@
//
// ImageCollectionViewCell.h
// Sample
//
// Created by Hannah Troisi on 1/28/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface ImageCollectionViewCell : UICollectionViewCell
@end

View File

@@ -0,0 +1,46 @@
//
// ImageCollectionViewCell.m
// Sample
//
// Created by Hannah Troisi on 1/28/17.
// Copyright © 2017 Facebook. All rights reserved.
//
#import "ImageCollectionViewCell.h"
@implementation ImageCollectionViewCell
{
UILabel *_title;
UILabel *_description;
}
- (id)initWithFrame:(CGRect)aRect
{
self = [super initWithFrame:aRect];
if (self) {
_title = [[UILabel alloc] init];
_title.text = @"UICollectionViewCell";
[self.contentView addSubview:_title];
_description = [[UILabel alloc] init];
_description.text = @"description for cell";
[self.contentView addSubview:_description];
self.contentView.backgroundColor = [UIColor orangeColor];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
[_title sizeToFit];
[_description sizeToFit];
CGRect frame = _title.frame;
frame.origin.y = _title.frame.size.height;
_description.frame = frame;
}
@end

View File

@@ -28,6 +28,9 @@
@property (assign, nonatomic) UIEdgeInsets interItemSpacing;
@property (assign, nonatomic) CGFloat headerHeight;
- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath;
- (CGSize)headerSizeForSection:(NSInteger)section;
@end
@protocol MosaicCollectionViewLayoutDelegate <ASCollectionDelegate>
@@ -35,7 +38,3 @@
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(MosaicCollectionViewLayout *)layout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface MosaicCollectionViewLayoutInspector : NSObject <ASCollectionViewLayoutInspecting>
@end

View File

@@ -54,7 +54,7 @@
top += _sectionInset.top;
if (_headerHeight > 0) {
CGSize headerSize = [self _headerSizeForSection:section];
CGSize headerSize = [self headerSizeForSection:section];
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes
layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]];
@@ -75,7 +75,7 @@
NSUInteger columnIndex = [self _shortestColumnIndexInSection:section];
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section];
CGSize itemSize = [self _itemSizeAtIndexPath:indexPath];
CGSize itemSize = [self itemSizeAtIndexPath:indexPath];
CGFloat xOffset = _sectionInset.left + (columnWidth + _columnSpacing) * columnIndex;
CGFloat yOffset = [_columnHeights[section][columnIndex] floatValue];
@@ -146,7 +146,7 @@
return ([self _widthForSection:section] - ((_numberOfColumns - 1) * _columnSpacing)) / _numberOfColumns;
}
- (CGSize)_itemSizeAtIndexPath:(NSIndexPath *)indexPath
- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath
{
CGSize size = CGSizeMake([self _columnWidthForSection:indexPath.section], 0);
CGSize originalSize = [[self _delegate] collectionView:self.collectionView layout:self originalItemSizeAtIndexPath:indexPath];
@@ -156,7 +156,7 @@
return size;
}
- (CGSize)_headerSizeForSection:(NSUInteger)section
- (CGSize)headerSizeForSection:(NSInteger)section
{
return CGSizeMake([self _widthForSection:section], _headerHeight);
}
@@ -199,36 +199,3 @@
}
@end
@implementation MosaicCollectionViewLayoutInspector
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout];
return ASSizeRangeMake(CGSizeZero, [layout _itemSizeAtIndexPath:indexPath]);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout];
return ASSizeRangeMake(CGSizeZero, [layout _headerSizeForSection:indexPath.section]);
}
- (ASScrollDirection)scrollableDirections
{
return ASScrollDirectionVerticalDirections;
}
/**
* Asks the inspector for the number of supplementary views for the given kind in the specified section.
*/
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
return 1;
} else {
return 0;
}
}
@end

View File

@@ -20,14 +20,17 @@
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "MosaicCollectionViewLayout.h"
#import "ImageCellNode.h"
#import "ImageCollectionViewCell.h"
// This option demonstrates that raw UIKit cells can still be used alongside native ASCellNodes.
static BOOL kShowUICollectionViewCells = YES;
static NSString *kReuseIdentifier = @"ImageCollectionViewCell";
static NSUInteger kNumberOfImages = 14;
@interface ViewController () <ASCollectionDataSource, ASCollectionDelegate>
@interface ViewController () <ASCollectionDataSourceInterop, ASCollectionDelegate, ASCollectionViewLayoutInspecting>
{
NSMutableArray *_sections;
ASCollectionNode *_collectionNode;
MosaicCollectionViewLayoutInspector *_layoutInspector;
}
@end
@@ -35,7 +38,7 @@ static NSUInteger kNumberOfImages = 14;
@implementation ViewController
#pragma mark -
#pragma mark UIViewController.
#pragma mark UIViewController
- (instancetype)init
{
@@ -48,8 +51,6 @@ static NSUInteger kNumberOfImages = 14;
_collectionNode.delegate = self;
_collectionNode.backgroundColor = [UIColor whiteColor];
_layoutInspector = [[MosaicCollectionViewLayoutInspector alloc] init];
if (!(self = [super initWithNode:_collectionNode]))
return nil;
@@ -73,7 +74,8 @@ static NSUInteger kNumberOfImages = 14;
{
[super viewDidLoad];
_collectionNode.view.layoutInspector = _layoutInspector;
_collectionNode.view.layoutInspector = self;
[_collectionNode.view registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier];
}
- (void)reloadTapped
@@ -85,6 +87,11 @@ static NSUInteger kNumberOfImages = 14;
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
{
if (kShowUICollectionViewCells && indexPath.item % 3 == 1) {
// When enabled, return nil for every third cell and then cellForItemAtIndexPath: will be called.
return nil;
}
UIImage *image = _sections[indexPath.section][indexPath.item];
return ^{
return [[ImageCellNode alloc] initWithImage:image];
@@ -92,6 +99,31 @@ static NSUInteger kNumberOfImages = 14;
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
{
MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout];
return ASSizeRangeMake(CGSizeZero, [layout itemSizeAtIndexPath:indexPath]);
}
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout];
return ASSizeRangeMake(CGSizeZero, [layout headerSizeForSection:indexPath.section]);
}
- (ASScrollDirection)scrollableDirections
{
return ASScrollDirectionVerticalDirections;
}
/**
* Asks the inspector for the number of supplementary views for the given kind in the specified section.
*/
- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section
{
return [kind isEqualToString:UICollectionElementKindSectionHeader] ? 1 : 0;
}
- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *textAttributes = @{
@@ -116,7 +148,22 @@ static NSUInteger kNumberOfImages = 14;
- (CGSize)collectionView:(ASCollectionNode *)collectionNode layout:(UICollectionViewLayout *)collectionViewLayout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath
{
return [(UIImage *)_sections[indexPath.section][indexPath.item] size];
ASCellNode *cellNode = [collectionNode nodeForItemAtIndexPath:indexPath];
if ([cellNode isKindOfClass:[ImageCellNode class]]) {
return [[(ImageCellNode *)cellNode image] size];
} else {
return CGSizeMake(100, 100); // In kShowUICollectionViewCells = YES mode, make those cells 100x100.
}
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
@end