diff --git a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj index d443a206f..c1014c643 100644 --- a/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj +++ b/examples/CustomCollectionView/Sample.xcodeproj/project.pbxproj @@ -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 */; }; @@ -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 = ""; }; - 25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionViewLayout.m; sourceTree = ""; }; + 25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionLayoutDelegate.m; sourceTree = ""; }; 25A1FA861C02FCB000193875 /* ImageCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCellNode.h; sourceTree = ""; }; 25A1FA871C02FCB000193875 /* ImageCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ImageCellNode.m; sourceTree = ""; }; 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Sample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -36,6 +35,7 @@ AC3C4A691A11F47200143C57 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; AC3C4A8D1A11F80C00143C57 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 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 = ""; }; + E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionLayoutDelegate.h; sourceTree = ""; }; 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 = ""; }; /* End PBXFileReference section */ @@ -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 */, @@ -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 */, diff --git a/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata b/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..7b5a2f305 --- /dev/null +++ b/examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples/CustomCollectionView/Sample/ImageCellNode.m b/examples/CustomCollectionView/Sample/ImageCellNode.m index 722d8def0..4292b0f18 100644 --- a/examples/CustomCollectionView/Sample/ImageCellNode.m +++ b/examples/CustomCollectionView/Sample/ImageCellNode.m @@ -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" @@ -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 diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h new file mode 100644 index 000000000..78a3f7a17 --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.h @@ -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 +#import + +@interface MosaicCollectionLayoutDelegate : NSObject + +- (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:(CGFloat)headerHeight; + +@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m new file mode 100644 index 000000000..211ad7cf3 --- /dev/null +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m @@ -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 + +@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 *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 diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h b/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h deleted file mode 100644 index 82649f736..000000000 --- a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// MosaicCollectionViewLayout.h -// Sample -// -// Created by McCallum, Levi on 11/22/15. -// -// 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. -// -// 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. -// - -#import -#import - -@interface MosaicCollectionViewLayout : UICollectionViewLayout - -@property (assign, nonatomic) NSUInteger numberOfColumns; -@property (assign, nonatomic) CGFloat columnSpacing; -@property (assign, nonatomic) UIEdgeInsets sectionInset; -@property (assign, nonatomic) UIEdgeInsets interItemSpacing; -@property (assign, nonatomic) CGFloat headerHeight; - -- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath; -- (CGSize)headerSizeForSection:(NSInteger)section; - -@end - -@protocol MosaicCollectionViewLayoutDelegate - -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(MosaicCollectionViewLayout *)layout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath; - -@end diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m b/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m deleted file mode 100644 index 633fd0fc6..000000000 --- a/examples/CustomCollectionView/Sample/MosaicCollectionViewLayout.m +++ /dev/null @@ -1,201 +0,0 @@ -// -// MosaicCollectionViewLayout.m -// Sample -// -// Created by McCallum, Levi on 11/22/15. -// -// 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. -// -// 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. -// - -#import "MosaicCollectionViewLayout.h" - -@implementation MosaicCollectionViewLayout { - NSMutableArray *_columnHeights; - NSMutableArray *_itemAttributes; - NSMutableDictionary *_headerAttributes; - NSMutableArray *_allAttributes; -} - -- (instancetype)init -{ - self = [super init]; - if (self != nil) { - self.numberOfColumns = 3; - self.columnSpacing = 10.0; - self.sectionInset = UIEdgeInsetsMake(10.0, 10.0, 10.0, 10.0); - self.interItemSpacing = UIEdgeInsetsMake(10.0, 0, 10.0, 0); - } - return self; -} - -- (void)prepareLayout -{ - _itemAttributes = [NSMutableArray array]; - _columnHeights = [NSMutableArray array]; - _allAttributes = [NSMutableArray array]; - _headerAttributes = [NSMutableDictionary dictionary]; - - CGFloat top = 0; - - NSInteger numberOfSections = [self.collectionView numberOfSections]; - for (NSUInteger section = 0; section < numberOfSections; section++) { - NSInteger numberOfItems = [self.collectionView numberOfItemsInSection:section]; - - top += _sectionInset.top; - - if (_headerHeight > 0) { - CGSize headerSize = [self headerSizeForSection:section]; - UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes - layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader - withIndexPath:[NSIndexPath indexPathForItem:0 inSection:section]]; - attributes.frame = CGRectMake(_sectionInset.left, top, headerSize.width, headerSize.height); - _headerAttributes[@(section)] = attributes; - [_allAttributes addObject:attributes]; - top = CGRectGetMaxY(attributes.frame); - } - - [_columnHeights addObject:[NSMutableArray array]]; - for (NSUInteger idx = 0; idx < self.numberOfColumns; idx++) { - [_columnHeights[section] addObject:@(top)]; - } - - CGFloat columnWidth = [self _columnWidthForSection:section]; - [_itemAttributes addObject:[NSMutableArray array]]; - for (NSUInteger idx = 0; idx < numberOfItems; idx++) { - NSUInteger columnIndex = [self _shortestColumnIndexInSection:section]; - NSIndexPath *indexPath = [NSIndexPath indexPathForItem:idx inSection:section]; - - CGSize itemSize = [self itemSizeAtIndexPath:indexPath]; - CGFloat xOffset = _sectionInset.left + (columnWidth + _columnSpacing) * columnIndex; - CGFloat yOffset = [_columnHeights[section][columnIndex] floatValue]; - - UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes - layoutAttributesForCellWithIndexPath:indexPath]; - attributes.frame = CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height); - - _columnHeights[section][columnIndex] = @(CGRectGetMaxY(attributes.frame) + _interItemSpacing.bottom); - - [_itemAttributes[section] addObject:attributes]; - [_allAttributes addObject:attributes]; - } - - NSUInteger columnIndex = [self _tallestColumnIndexInSection:section]; - top = [_columnHeights[section][columnIndex] floatValue] - _interItemSpacing.bottom + _sectionInset.bottom; - - for (NSUInteger idx = 0; idx < [_columnHeights[section] count]; idx++) { - _columnHeights[section][idx] = @(top); - } - } -} - -- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect -{ - NSMutableArray *includedAttributes = [NSMutableArray array]; - // Slow search for small batches - for (UICollectionViewLayoutAttributes *attributes in _allAttributes) { - if (CGRectIntersectsRect(attributes.frame, rect)) { - [includedAttributes addObject:attributes]; - } - } - return includedAttributes; -} - -- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section >= _itemAttributes.count) { - return nil; - } else if (indexPath.item >= [_itemAttributes[indexPath.section] count]) { - return nil; - } - return _itemAttributes[indexPath.section][indexPath.item]; -} - -- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath -{ - if ([elementKind isEqualToString:UICollectionElementKindSectionHeader]) { - return _headerAttributes[@(indexPath.section)]; - } - return nil; -} - -- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds -{ - if (!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size)) { - return YES; - } - return NO; -} - -- (CGFloat)_widthForSection:(NSUInteger)section -{ - return self.collectionView.bounds.size.width - _sectionInset.left - _sectionInset.right; -} - -- (CGFloat)_columnWidthForSection:(NSUInteger)section -{ - return ([self _widthForSection:section] - ((_numberOfColumns - 1) * _columnSpacing)) / _numberOfColumns; -} - -- (CGSize)itemSizeAtIndexPath:(NSIndexPath *)indexPath -{ - CGSize size = CGSizeMake([self _columnWidthForSection:indexPath.section], 0); - CGSize originalSize = [[self _delegate] collectionView:self.collectionView layout:self originalItemSizeAtIndexPath:indexPath]; - if (originalSize.height > 0 && originalSize.width > 0) { - size.height = originalSize.height / originalSize.width * size.width; - } - return size; -} - -- (CGSize)headerSizeForSection:(NSInteger)section -{ - return CGSizeMake([self _widthForSection:section], _headerHeight); -} - -- (CGSize)collectionViewContentSize -{ - CGFloat height = [[[_columnHeights lastObject] firstObject] floatValue]; - return CGSizeMake(self.collectionView.bounds.size.width, height); -} - -- (NSUInteger)_tallestColumnIndexInSection:(NSUInteger)section -{ - __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 -{ - __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; -} - -- (id)_delegate -{ - return (id)self.collectionView.delegate; -} - -@end diff --git a/examples/CustomCollectionView/Sample/ViewController.m b/examples/CustomCollectionView/Sample/ViewController.m index e9e59f6b3..fea379316 100644 --- a/examples/CustomCollectionView/Sample/ViewController.m +++ b/examples/CustomCollectionView/Sample/ViewController.m @@ -1,24 +1,25 @@ // // ViewController.m -// Sample +// 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. // -// 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. +// 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 +// +// http://www.apache.org/licenses/LICENSE-2.0 // #import "ViewController.h" #import -#import "MosaicCollectionViewLayout.h" +#import +#import "MosaicCollectionLayoutDelegate.h" #import "ImageCellNode.h" #import "ImageCollectionViewCell.h" @@ -42,11 +43,8 @@ @implementation ViewController - (instancetype)init { - MosaicCollectionViewLayout *layout = [[MosaicCollectionViewLayout alloc] init]; - layout.numberOfColumns = 2; - layout.headerHeight = 44.0; - - _collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout]; + MosaicCollectionLayoutDelegate *layoutDelegate = [[MosaicCollectionLayoutDelegate alloc] initWithNumberOfColumns:2 headerHeight:44.0]; + _collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil]; _collectionNode.dataSource = self; _collectionNode.delegate = self; _collectionNode.backgroundColor = [UIColor whiteColor]; @@ -98,17 +96,11 @@ - (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockFo }; } - +// 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 { - 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]); + return ASSizeRangeZero; } - (ASScrollDirection)scrollableDirections @@ -146,16 +138,6 @@ - (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSe return [_sections[section] count]; } -- (CGSize)collectionView:(ASCollectionNode *)collectionNode layout:(UICollectionViewLayout *)collectionViewLayout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath -{ - 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];