Add 'submodules/AsyncDisplayKit/' from commit '02bedc12816e251ad71777f9d2578329b6d2bef6'
git-subtree-dir: submodules/AsyncDisplayKit git-subtree-mainline:d06f423e0egit-subtree-split:02bedc1281
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property (strong, nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "ViewController.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
self.window.rootViewController = [[ViewController alloc] init];
|
||||
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// ImageCellNode.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ImageCellNode : ASCellNode
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image;
|
||||
@property (nonatomic, strong) UIImage *image;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// ImageCellNode.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "ImageCellNode.h"
|
||||
|
||||
@implementation ImageCellNode {
|
||||
ASImageNode *_imageNode;
|
||||
}
|
||||
|
||||
- (id)initWithImage:(UIImage *)image
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_imageNode = [[ASImageNode alloc] init];
|
||||
_imageNode.image = image;
|
||||
[self addSubnode:_imageNode];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGSize imageSize = self.image.size;
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero
|
||||
child:[ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageSize.height/imageSize.width
|
||||
child:_imageNode]];
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)image
|
||||
{
|
||||
_imageNode.image = image;
|
||||
}
|
||||
|
||||
- (UIImage *)image
|
||||
{
|
||||
return _imageNode.image;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ImageCollectionViewCell.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface ImageCollectionViewCell : UICollectionViewCell
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// ImageCollectionViewCell.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Default-568h@2x.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "1x",
|
||||
"orientation" : "portrait"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"orientation" : "portrait"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Default-568h@2x.png",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_0.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 MiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_1.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 114 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_10.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 516 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_11.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 434 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_12.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 78 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_13.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 168 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_2.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 540 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_3.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_4.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 111 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_5.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 892 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_6.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 494 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_7.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 250 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_8.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "image_9.jpg"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 429 KiB |
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIcons</key>
|
||||
<dict/>
|
||||
<key>CFBundleIcons~ipad</key>
|
||||
<dict/>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.facebook.AsyncDisplayKit.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>Launchboard</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
|
||||
</dependencies>
|
||||
<scenes/>
|
||||
</document>
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// MosaicCollectionLayoutDelegate.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface MosaicCollectionLayoutDelegate : NSObject <ASCollectionLayoutDelegate>
|
||||
|
||||
- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// MosaicCollectionLayoutDelegate.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "MosaicCollectionLayoutDelegate.h"
|
||||
#import "MosaicCollectionLayoutInfo.h"
|
||||
#import "ImageCellNode.h"
|
||||
|
||||
#import <AsyncDisplayKit/ASCollectionElement.h>
|
||||
|
||||
@implementation MosaicCollectionLayoutDelegate {
|
||||
// Read-only properties
|
||||
MosaicCollectionLayoutInfo *_info;
|
||||
}
|
||||
|
||||
- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_info = [[MosaicCollectionLayoutInfo alloc] initWithNumberOfColumns:numberOfColumns
|
||||
headerHeight:headerHeight
|
||||
columnSpacing:10.0
|
||||
sectionInsets:UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0)
|
||||
interItemSpacing:UIEdgeInsetsMake(10.0, 0, 10.0, 0)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASScrollDirection)scrollableDirections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return ASScrollDirectionVerticalDirections;
|
||||
}
|
||||
|
||||
- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
return _info;
|
||||
}
|
||||
|
||||
+ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context
|
||||
{
|
||||
CGFloat layoutWidth = context.viewportSize.width;
|
||||
ASElementMap *elements = context.elements;
|
||||
CGFloat top = 0;
|
||||
MosaicCollectionLayoutInfo *info = (MosaicCollectionLayoutInfo *)context.additionalInfo;
|
||||
|
||||
NSMapTable<ASCollectionElement *, UICollectionViewLayoutAttributes *> *attrsMap = [NSMapTable elementToLayoutAttributesTable];
|
||||
NSMutableArray *columnHeights = [NSMutableArray array];
|
||||
|
||||
NSInteger numberOfSections = [elements numberOfSections];
|
||||
for (NSUInteger section = 0; section < numberOfSections; section++) {
|
||||
NSInteger numberOfItems = [elements numberOfItemsInSection:section];
|
||||
|
||||
top += info.sectionInsets.top;
|
||||
|
||||
if (info.headerHeight > 0) {
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:section];
|
||||
ASCollectionElement *element = [elements supplementaryElementOfKind:UICollectionElementKindSectionHeader
|
||||
atIndexPath:indexPath];
|
||||
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
|
||||
withIndexPath:indexPath];
|
||||
|
||||
ASSizeRange sizeRange = [self _sizeRangeForHeaderOfSection:section withLayoutWidth:layoutWidth info:info];
|
||||
CGSize size = [element.node layoutThatFits:sizeRange].size;
|
||||
CGRect frame = CGRectMake(info.sectionInsets.left, top, size.width, size.height);
|
||||
|
||||
attrs.frame = frame;
|
||||
[attrsMap setObject:attrs forKey:element];
|
||||
top = CGRectGetMaxY(frame);
|
||||
}
|
||||
|
||||
[columnHeights addObject:[NSMutableArray array]];
|
||||
for (NSUInteger idx = 0; idx < info.numberOfColumns; idx++) {
|
||||
[columnHeights[section] addObject:@(top)];
|
||||
}
|
||||
|
||||
CGFloat columnWidth = [self _columnWidthForSection:section withLayoutWidth:layoutWidth info:info];
|
||||
for (NSUInteger idx = 0; idx < numberOfItems; idx++) {
|
||||
NSUInteger columnIndex = [self _shortestColumnIndexInSection:section withColumnHeights:columnHeights];
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section];
|
||||
ASCollectionElement *element = [elements elementForItemAtIndexPath:indexPath];
|
||||
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
|
||||
|
||||
ASSizeRange sizeRange = [self _sizeRangeForItem:element.node atIndexPath:indexPath withLayoutWidth:layoutWidth info:info];
|
||||
CGSize size = [element.node layoutThatFits:sizeRange].size;
|
||||
CGPoint position = CGPointMake(info.sectionInsets.left + (columnWidth + info.columnSpacing) * columnIndex,
|
||||
[columnHeights[section][columnIndex] floatValue]);
|
||||
CGRect frame = CGRectMake(position.x, position.y, size.width, size.height);
|
||||
|
||||
attrs.frame = frame;
|
||||
[attrsMap setObject:attrs forKey:element];
|
||||
// TODO Profile and avoid boxing if there are significant retain/release overheads
|
||||
columnHeights[section][columnIndex] = @(CGRectGetMaxY(frame) + info.interItemSpacing.bottom);
|
||||
}
|
||||
|
||||
NSUInteger columnIndex = [self _tallestColumnIndexInSection:section withColumnHeights:columnHeights];
|
||||
top = [columnHeights[section][columnIndex] floatValue] - info.interItemSpacing.bottom + info.sectionInsets.bottom;
|
||||
|
||||
for (NSUInteger idx = 0; idx < [columnHeights[section] count]; idx++) {
|
||||
columnHeights[section][idx] = @(top);
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat contentHeight = [[[columnHeights lastObject] firstObject] floatValue];
|
||||
CGSize contentSize = CGSizeMake(layoutWidth, contentHeight);
|
||||
return [[ASCollectionLayoutState alloc] initWithContext:context
|
||||
contentSize:contentSize
|
||||
elementToLayoutAttributesTable:attrsMap];
|
||||
}
|
||||
|
||||
+ (CGFloat)_columnWidthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info
|
||||
{
|
||||
return ([self _widthForSection:section withLayoutWidth:layoutWidth info:info] - ((info.numberOfColumns - 1) * info.columnSpacing)) / info.numberOfColumns;
|
||||
}
|
||||
|
||||
+ (CGFloat)_widthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info
|
||||
{
|
||||
return layoutWidth - info.sectionInsets.left - info.sectionInsets.right;
|
||||
}
|
||||
|
||||
+ (ASSizeRange)_sizeRangeForItem:(ASCellNode *)item atIndexPath:(NSIndexPath *)indexPath withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info
|
||||
{
|
||||
CGFloat itemWidth = [self _columnWidthForSection:indexPath.section withLayoutWidth:layoutWidth info:info];
|
||||
if ([item isKindOfClass:[ImageCellNode class]]) {
|
||||
return ASSizeRangeMake(CGSizeMake(itemWidth, 0), CGSizeMake(itemWidth, CGFLOAT_MAX));
|
||||
} else {
|
||||
return ASSizeRangeMake(CGSizeMake(itemWidth, itemWidth)); // In kShowUICollectionViewCells = YES mode, make those cells itemWidth x itemWidth.
|
||||
}
|
||||
}
|
||||
|
||||
+ (ASSizeRange)_sizeRangeForHeaderOfSection:(NSInteger)section withLayoutWidth:(CGFloat)layoutWidth info:(MosaicCollectionLayoutInfo *)info
|
||||
{
|
||||
return ASSizeRangeMake(CGSizeMake(0, info.headerHeight), CGSizeMake([self _widthForSection:section withLayoutWidth:layoutWidth info:info], info.headerHeight));
|
||||
}
|
||||
|
||||
+ (NSUInteger)_tallestColumnIndexInSection:(NSUInteger)section withColumnHeights:(NSArray *)columnHeights
|
||||
{
|
||||
__block NSUInteger index = 0;
|
||||
__block CGFloat tallestHeight = 0;
|
||||
[columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) {
|
||||
if (height.floatValue > tallestHeight) {
|
||||
index = idx;
|
||||
tallestHeight = height.floatValue;
|
||||
}
|
||||
}];
|
||||
return index;
|
||||
}
|
||||
|
||||
+ (NSUInteger)_shortestColumnIndexInSection:(NSUInteger)section withColumnHeights:(NSArray *)columnHeights
|
||||
{
|
||||
__block NSUInteger index = 0;
|
||||
__block CGFloat shortestHeight = CGFLOAT_MAX;
|
||||
[columnHeights[section] enumerateObjectsUsingBlock:^(NSNumber *height, NSUInteger idx, BOOL *stop) {
|
||||
if (height.floatValue < shortestHeight) {
|
||||
index = idx;
|
||||
shortestHeight = height.floatValue;
|
||||
}
|
||||
}];
|
||||
return index;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// MosaicCollectionLayoutInfo.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface MosaicCollectionLayoutInfo : NSObject
|
||||
|
||||
// Read-only properties
|
||||
@property (nonatomic, assign, readonly) NSInteger numberOfColumns;
|
||||
@property (nonatomic, assign, readonly) CGFloat headerHeight;
|
||||
@property (nonatomic, assign, readonly) CGFloat columnSpacing;
|
||||
@property (nonatomic, assign, readonly) UIEdgeInsets sectionInsets;
|
||||
@property (nonatomic, assign, readonly) UIEdgeInsets interItemSpacing;
|
||||
|
||||
- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns
|
||||
headerHeight:(CGFloat)headerHeight
|
||||
columnSpacing:(CGFloat)columnSpacing
|
||||
sectionInsets:(UIEdgeInsets)sectionInsets
|
||||
interItemSpacing:(UIEdgeInsets)interItemSpacing NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)init __unavailable;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// MosaicCollectionLayoutInfo.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "MosaicCollectionLayoutInfo.h"
|
||||
|
||||
#import <AsyncDisplayKit/ASHashing.h>
|
||||
|
||||
@implementation MosaicCollectionLayoutInfo
|
||||
|
||||
- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns
|
||||
headerHeight:(CGFloat)headerHeight
|
||||
columnSpacing:(CGFloat)columnSpacing
|
||||
sectionInsets:(UIEdgeInsets)sectionInsets
|
||||
interItemSpacing:(UIEdgeInsets)interItemSpacing
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_numberOfColumns = numberOfColumns;
|
||||
_headerHeight = headerHeight;
|
||||
_columnSpacing = columnSpacing;
|
||||
_sectionInsets = sectionInsets;
|
||||
_interItemSpacing = interItemSpacing;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToInfo:(MosaicCollectionLayoutInfo *)info
|
||||
{
|
||||
if (info == nil) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return _numberOfColumns == info.numberOfColumns
|
||||
&& _headerHeight == info.headerHeight
|
||||
&& _columnSpacing == info.columnSpacing
|
||||
&& UIEdgeInsetsEqualToEdgeInsets(_sectionInsets, info.sectionInsets)
|
||||
&& UIEdgeInsetsEqualToEdgeInsets(_interItemSpacing, info.interItemSpacing);
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (self == other) {
|
||||
return YES;
|
||||
}
|
||||
if (! [other isKindOfClass:[MosaicCollectionLayoutInfo class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToInfo:other];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
struct {
|
||||
NSInteger numberOfColumns;
|
||||
CGFloat headerHeight;
|
||||
CGFloat columnSpacing;
|
||||
UIEdgeInsets sectionInsets;
|
||||
UIEdgeInsets interItemSpacing;
|
||||
} data = {
|
||||
_numberOfColumns,
|
||||
_headerHeight,
|
||||
_columnSpacing,
|
||||
_sectionInsets,
|
||||
_interItemSpacing,
|
||||
};
|
||||
return ASHashBytes(&data, sizeof(data));
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// ViewController.h
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
|
||||
@interface ViewController : ASViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// ViewController.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import "ViewController.h"
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import <AsyncDisplayKit/ASCollectionNode+Beta.h>
|
||||
#import "MosaicCollectionLayoutDelegate.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 () <ASCollectionDataSourceInterop, ASCollectionDelegate, ASCollectionViewLayoutInspecting>
|
||||
{
|
||||
NSMutableArray *_sections;
|
||||
ASCollectionNode *_collectionNode;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark UIViewController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
MosaicCollectionLayoutDelegate *layoutDelegate = [[MosaicCollectionLayoutDelegate alloc] initWithNumberOfColumns:2 headerHeight:44.0];
|
||||
_collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil];
|
||||
_collectionNode.dataSource = self;
|
||||
_collectionNode.delegate = self;
|
||||
_collectionNode.layoutInspector = self;
|
||||
|
||||
if (!(self = [super initWithNode:_collectionNode]))
|
||||
return nil;
|
||||
|
||||
_sections = [NSMutableArray array];
|
||||
[_sections addObject:[NSMutableArray array]];
|
||||
for (NSUInteger idx = 0, section = 0; idx < kNumberOfImages; idx++) {
|
||||
NSString *name = [NSString stringWithFormat:@"image_%lu.jpg", (unsigned long)idx];
|
||||
[_sections[section] addObject:[UIImage imageNamed:name]];
|
||||
if ((idx + 1) % 5 == 0 && idx < kNumberOfImages - 1) {
|
||||
section++;
|
||||
[_sections addObject:[NSMutableArray array]];
|
||||
}
|
||||
}
|
||||
|
||||
[_collectionNode registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[_collectionNode.view registerClass:[ImageCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier];
|
||||
}
|
||||
|
||||
- (void)reloadTapped
|
||||
{
|
||||
[_collectionNode reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - ASCollectionNode data source.
|
||||
|
||||
- (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];
|
||||
};
|
||||
}
|
||||
|
||||
// The below 2 methods are required by ASCollectionViewLayoutInspecting, but ASCollectionLayout and its layout delegate are the ones that really determine the size ranges and directions
|
||||
// TODO Remove these methods once a layout inspector is no longer required under ASCollectionLayout mode
|
||||
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return ASSizeRangeZero;
|
||||
}
|
||||
|
||||
- (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 = @{
|
||||
NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline],
|
||||
NSForegroundColorAttributeName: [UIColor grayColor]
|
||||
};
|
||||
UIEdgeInsets textInsets = UIEdgeInsetsMake(11.0, 0, 11.0, 0);
|
||||
ASTextCellNode *textCellNode = [[ASTextCellNode alloc] initWithAttributes:textAttributes insets:textInsets];
|
||||
textCellNode.text = [NSString stringWithFormat:@"Section %zd", indexPath.section + 1];
|
||||
return textCellNode;
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionNode:(ASCollectionNode *)collectionNode
|
||||
{
|
||||
return _sections.count;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return [_sections[section] count];
|
||||
}
|
||||
|
||||
- (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
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// main.m
|
||||
// Texture
|
||||
//
|
||||
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
|
||||
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
|
||||
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||