mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
[example/CustomCollectionView] Implement MosaicCollectionLayoutDelegate (#28)
* Implement MosaicCollectionLayoutDelegate * Update licenses * Address comments * Fix license
This commit is contained in:
@@ -7,7 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* 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 */; };
|
25A1FA881C02FCB000193875 /* ImageCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A1FA871C02FCB000193875 /* ImageCellNode.m */; };
|
||||||
576F970133B34DFD583D5CE4 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */; };
|
576F970133B34DFD583D5CE4 /* libPods-Sample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CC0FB9EE0030992E8FBC0A0 /* libPods-Sample.a */; };
|
||||||
80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */; };
|
80364CCA1E3D95A90094400C /* ImageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 80364CC91E3D95A90094400C /* ImageCollectionViewCell.m */; };
|
||||||
@@ -19,8 +19,7 @@
|
|||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MosaicCollectionViewLayout.h; sourceTree = "<group>"; };
|
25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionLayoutDelegate.m; sourceTree = "<group>"; };
|
||||||
25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MosaicCollectionViewLayout.m; sourceTree = "<group>"; };
|
|
||||||
25A1FA861C02FCB000193875 /* ImageCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ImageCellNode.h; 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>"; };
|
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; };
|
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 = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -81,8 +81,8 @@
|
|||||||
AC3C4A601A11F47200143C57 /* Sample */ = {
|
AC3C4A601A11F47200143C57 /* Sample */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
25A1FA831C02F7AC00193875 /* MosaicCollectionViewLayout.h */,
|
E5D73A3A1EA6766B006418A8 /* MosaicCollectionLayoutDelegate.h */,
|
||||||
25A1FA841C02F7AC00193875 /* MosaicCollectionViewLayout.m */,
|
25A1FA841C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m */,
|
||||||
AC3C4A651A11F47200143C57 /* AppDelegate.h */,
|
AC3C4A651A11F47200143C57 /* AppDelegate.h */,
|
||||||
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
|
AC3C4A661A11F47200143C57 /* AppDelegate.m */,
|
||||||
AC3C4A681A11F47200143C57 /* ViewController.h */,
|
AC3C4A681A11F47200143C57 /* ViewController.h */,
|
||||||
@@ -238,7 +238,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
25A1FA851C02F7AC00193875 /* MosaicCollectionViewLayout.m in Sources */,
|
25A1FA851C02F7AC00193875 /* MosaicCollectionLayoutDelegate.m in Sources */,
|
||||||
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
|
AC3C4A6A1A11F47200143C57 /* ViewController.m in Sources */,
|
||||||
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
|
AC3C4A671A11F47200143C57 /* AppDelegate.m in Sources */,
|
||||||
AC3C4A641A11F47200143C57 /* main.m in Sources */,
|
AC3C4A641A11F47200143C57 /* main.m in Sources */,
|
||||||
|
|||||||
10
examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
10
examples/CustomCollectionView/Sample.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:Sample.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
//
|
//
|
||||||
// ImageCellNode.m
|
// ImageCellNode.m
|
||||||
// Sample
|
// Texture
|
||||||
//
|
|
||||||
// Created by McCallum, Levi on 11/22/15.
|
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// 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
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
// you may not use this file except in compliance with the License.
|
||||||
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
// You may obtain a copy of the License at
|
||||||
// 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"
|
#import "ImageCellNode.h"
|
||||||
@@ -36,7 +34,10 @@
|
|||||||
|
|
||||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
- (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
|
- (void)setImage:(UIImage *)image
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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 <UIKit/UIKit.h>
|
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
|
||||||
|
|
||||||
@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 <ASCollectionDelegate>
|
|
||||||
|
|
||||||
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(MosaicCollectionViewLayout *)layout originalItemSizeAtIndexPath:(NSIndexPath *)indexPath;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -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<MosaicCollectionViewLayoutDelegate>)_delegate
|
|
||||||
{
|
|
||||||
return (id<MosaicCollectionViewLayoutDelegate>)self.collectionView.delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,24 +1,25 @@
|
|||||||
//
|
//
|
||||||
// ViewController.m
|
// ViewController.m
|
||||||
// Sample
|
// Texture
|
||||||
//
|
//
|
||||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||||
// This source code is licensed under the BSD-style license found in the
|
// 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
|
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
|
||||||
// of patent rights can be found in the PATENTS file in the same directory.
|
// 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
|
// Modifications to this file made after 4/13/2017 are: Copyright (c) 2017-present,
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
// you may not use this file except in compliance with the License.
|
||||||
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
// You may obtain a copy of the License at
|
||||||
// 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 "ViewController.h"
|
#import "ViewController.h"
|
||||||
|
|
||||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||||
#import "MosaicCollectionViewLayout.h"
|
#import <AsyncDisplayKit/ASCollectionNode+Beta.h>
|
||||||
|
#import "MosaicCollectionLayoutDelegate.h"
|
||||||
#import "ImageCellNode.h"
|
#import "ImageCellNode.h"
|
||||||
#import "ImageCollectionViewCell.h"
|
#import "ImageCollectionViewCell.h"
|
||||||
|
|
||||||
@@ -42,11 +43,8 @@ static NSUInteger kNumberOfImages = 14;
|
|||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
MosaicCollectionViewLayout *layout = [[MosaicCollectionViewLayout alloc] init];
|
MosaicCollectionLayoutDelegate *layoutDelegate = [[MosaicCollectionLayoutDelegate alloc] initWithNumberOfColumns:2 headerHeight:44.0];
|
||||||
layout.numberOfColumns = 2;
|
_collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil];
|
||||||
layout.headerHeight = 44.0;
|
|
||||||
|
|
||||||
_collectionNode = [[ASCollectionNode alloc] initWithCollectionViewLayout:layout];
|
|
||||||
_collectionNode.dataSource = self;
|
_collectionNode.dataSource = self;
|
||||||
_collectionNode.delegate = self;
|
_collectionNode.delegate = self;
|
||||||
_collectionNode.backgroundColor = [UIColor whiteColor];
|
_collectionNode.backgroundColor = [UIColor whiteColor];
|
||||||
@@ -98,17 +96,11 @@ static NSUInteger kNumberOfImages = 14;
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
MosaicCollectionViewLayout *layout = (MosaicCollectionViewLayout *)[collectionView collectionViewLayout];
|
return ASSizeRangeZero;
|
||||||
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
|
- (ASScrollDirection)scrollableDirections
|
||||||
@@ -146,16 +138,6 @@ static NSUInteger kNumberOfImages = 14;
|
|||||||
return [_sections[section] count];
|
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
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||||
{
|
{
|
||||||
return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
return [_collectionNode.view dequeueReusableCellWithReuseIdentifier:kReuseIdentifier forIndexPath:indexPath];
|
||||||
|
|||||||
Reference in New Issue
Block a user