Skip to content

Commit

Permalink
[example/CustomCollectionView] Implement MosaicCollectionLayoutDelega…
Browse files Browse the repository at this point in the history
…te (#28)

* Implement MosaicCollectionLayoutDelegate

* Update licenses

* Address comments

* Fix license
  • Loading branch information
nguyenhuy authored and Adlai-Holler committed May 14, 2017
1 parent d4725a5 commit 7dd8361
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 293 deletions.
12 changes: 6 additions & 6 deletions examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */; };
25A1FA851C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.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 */; };
Expand All @@ -19,8 +19,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionViewLayout.h; sourceTree = "<group>"; };
25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionViewLayout.m; sourceTree = "<group>"; };
25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionLayoutDelegate.m; sourceTree = "<group>"; };
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; };
Expand All @@ -36,6 +35,7 @@
AC3C4A691A11F47200143C57 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
E2F287D91FFDEA2A747630CE /* Pods-Sample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.release.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.release.xcconfig"; sourceTree = "<group>"; };
E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionLayoutDelegate.h; sourceTree = "<group>"; };
F36BCD8EBAF79797AB5C6708 /* Pods-Sample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Sample/Pods-Sample.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -81,8 +81,8 @@
AC3C4A601A11F47200143C57 /* Sample */ = {
isa = PBXGroup;
children = (
25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */,
25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */,
E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */,
25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */,
AC3C4A651A11F47200143C57 /* AppDelegate.h */,
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
AC3C4A681A11F47200143C57 /* ViewController.h */,
Expand Down Expand Up @@ -238,7 +238,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */,
25A1FA851C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m in Sources */,
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
AC3C4A641A11F47200143C57 /* main.m in Sources */,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 13 additions & 12 deletions examples/CustomCollectionView/Sample/ImageCellNode.m
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
//
// ImageCellNode.m
// Sample
//
// Created by McCallum, Levi on 11/22/15.
// Texture
//
// 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.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// http://www.apache.org/licenses/LICENSE-2.0
//

#import "ImageCellNode.h"
Expand All @@ -36,7 +34,10 @@ - (id)initWithImage:(UIImage *)image

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_imageNode];
CGSize imageSize = self.image.size;
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero
child:[ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageSize.height/imageSize.width
child:_imageNode]];
}

- (void)setImage:(UIImage *)image
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// MosaicCollectionLayoutDelegate.h
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 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
165 changes: 165 additions & 0 deletions examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//
// MosaicCollectionLayoutDelegate.m
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//

#import "MosaicCollectionLayoutDelegate.h"
#import "ImageCellNode.h"

#import <AsyncDisplayKit/ASCollectionElement.h>

@implementation MosaicCollectionLayoutDelegate {
// Read-only properties
NSInteger _numberOfColumns;
CGFloat _headerHeight;
CGFloat _columnSpacing;
UIEdgeInsets _sectionInset;
UIEdgeInsets _interItemSpacing;
}

- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight
{
self = [super init];
if (self != nil) {
_numberOfColumns = numberOfColumns;
_headerHeight = headerHeight;
_columnSpacing = 10.0;
_sectionInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0);
_interItemSpacing = UIEdgeInsetsMake(10.0, 0, 10.0, 0);
}
return self;
}

- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements
{
return nil;
}

- (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context
{
CGFloat layoutWidth = context.viewportSize.width;
ASElementMap *elements = context.elements;
CGFloat top = 0;

// TODO use +[NSMapTable elementToLayoutAttributesTable]
NSMapTable<ASCollectionElement *, UICollectionViewLayoutAttributes *> *attrsMap = [NSMapTable mapTableWithKeyOptions:(NSMapTableObjectPointerPersonality | NSMapTableWeakMemory) valueOptions:NSMapTableStrongMemory];
NSMutableArray *columnHeights = [NSMutableArray array];

NSInteger numberOfSections = [elements numberOfSections];
for (NSUInteger section = 0; section < numberOfSections; section++) {
NSInteger numberOfItems = [elements numberOfItemsInSection:section];

top += _sectionInset.top;

if (_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];
CGSize size = [element.node layoutThatFits:sizeRange].size;
CGRect frame = CGRectMake(_sectionInset.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 < _numberOfColumns; idx++) {
[columnHeights[section] addObject:@(top)];
}

CGFloat columnWidth = [self _columnWidthForSection:section withLayoutWidth:layoutWidth];
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];
CGSize size = [element.node layoutThatFits:sizeRange].size;
CGPoint position = CGPointMake(_sectionInset.left + (columnWidth + _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) + _interItemSpacing.bottom);
}

NSUInteger columnIndex = [self _tallestColumnIndexInSection:section withColumnHeights:columnHeights];
top = [columnHeights[section][columnIndex] floatValue] - _interItemSpacing.bottom + _sectionInset.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] initWithElements:elements contentSize:contentSize elementToLayoutArrtibutesMap:attrsMap];
}

- (CGFloat)_widthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth
{
return layoutWidth - _sectionInset.left - _sectionInset.right;
}

- (CGFloat)_columnWidthForSection:(NSUInteger)section withLayoutWidth:(CGFloat)layoutWidth
{
return ([self _widthForSection:section withLayoutWidth:layoutWidth] - ((_numberOfColumns - 1) * _columnSpacing)) / _numberOfColumns;
}

- (ASSizeRange)sizeRangeForItem:(ASCellNode *)item atIndexPath:(NSIndexPath *)indexPath withLayoutWidth:(CGFloat)layoutWidth;
{
CGFloat itemWidth = [self _columnWidthForSection:indexPath.section withLayoutWidth:layoutWidth];
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
{
return ASSizeRangeMake(CGSizeMake(0, _headerHeight), CGSizeMake([self _widthForSection:section withLayoutWidth:layoutWidth], _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
40 changes: 0 additions & 40 deletions examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h

This file was deleted.

Loading

0 comments on commit 7dd8361

Please sign in to comment.