Skip to content

Commit

Permalink
[ASCollectionLayout] Add ASCollectionGalleryLayoutSizeProviding (#451)
Browse files Browse the repository at this point in the history
* Add ASCollectionGalleryLayoutSizeProviding
- This allows users to return different sizes based on certain conditions, such as the collection node's bounds or grid constants.
- ASPagerNode will also act as a size provider to ensure all pages have an up-to-date size that is its bounds.

* Update CHANGELOG

* ASPagerNode to use gallery layout delegate if told to
  • Loading branch information
nguyenhuy authored Jul 18, 2017
1 parent 7af8f91 commit 78c133e
Show file tree
Hide file tree
Showing 14 changed files with 117 additions and 24 deletions.
4 changes: 4 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
DEFAD8131CC48914000527C4 /* ASVideoNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */; };
E51B78BF1F028ABF00E32604 /* ASLayoutFlatteningTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */; };
E54E00721F1D3828000B30D7 /* ASPagerNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = E54E00711F1D3828000B30D7 /* ASPagerNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
E54E81FC1EB357BD00FFE8E1 /* ASPageTable.h in Headers */ = {isa = PBXBuildFile; fileRef = E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */; };
E54E81FD1EB357BD00FFE8E1 /* ASPageTable.m in Sources */ = {isa = PBXBuildFile; fileRef = E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */; };
E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */; };
Expand Down Expand Up @@ -912,6 +913,7 @@
E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutFlatteningTests.m; sourceTree = "<group>"; };
E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutTransition.mm; sourceTree = "<group>"; };
E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutTransition.h; sourceTree = "<group>"; };
E54E00711F1D3828000B30D7 /* ASPagerNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASPagerNode+Beta.h"; sourceTree = "<group>"; };
E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPageTable.h; sourceTree = "<group>"; };
E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPageTable.m; sourceTree = "<group>"; };
E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutElement.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1101,6 +1103,7 @@
698371DA1E4379CD00437585 /* ASNodeController+Beta.m */,
25E327541C16819500A2170C /* ASPagerNode.h */,
25E327551C16819500A2170C /* ASPagerNode.m */,
E54E00711F1D3828000B30D7 /* ASPagerNode+Beta.h */,
A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */,
A2763D781CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.m */,
CCBBBF5C1EB161760069AA91 /* ASRangeManagingNode.h */,
Expand Down Expand Up @@ -1708,6 +1711,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
E54E00721F1D3828000B30D7 /* ASPagerNode+Beta.h in Headers */,
E5B225281F1790D6001E1431 /* ASHashing.h in Headers */,
CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */,
693A1DCA1ECC944E00D0C9D2 /* IGListAdapter+AsyncDisplayKit.h in Headers */,
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
- [ASCollectionView] Add delegate bridging and index space translation for missing UICollectionViewLayout properties. [Scott Goodson](https://github.com/appleguy)
- [ASTextNode2] Add initial implementation for link handling. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/396)
- [ASTextNode2] Provide compile flag to globally enable new implementation of ASTextNode: ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE. [Scott Goodson](https://github.com/appleguy) [#396](https://github.com/TextureGroup/Texture/pull/410)
- Add ASCollectionGalleryLayoutDelegate - an async collection layout that makes same-size collections (e.g photo galleries, pagers, etc) fast and lightweight! [Huy Nguyen](https://github.com/nguyenhuy/) [#76](https://github.com/TextureGroup/Texture/pull/76)
- Add ASCollectionGalleryLayoutDelegate - an async collection layout that makes same-size collections (e.g photo galleries, pagers, etc) fast and lightweight! [Huy Nguyen](https://github.com/nguyenhuy/) [#76](https://github.com/TextureGroup/Texture/pull/76) [#451](https://github.com/TextureGroup/Texture/pull/451)

##2.3.5
- Fix an issue where inserting/deleting sections could lead to inconsistent supplementary element behavior. [Adlai Holler](https://github.com/Adlai-Holler)
Expand Down
19 changes: 19 additions & 0 deletions Source/ASPagerNode+Beta.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ASPagerNode+Beta.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 <AsyncDisplayKit/ASPagerNode.h>

@interface ASPagerNode (Beta)

- (instancetype)initUsingAsyncCollectionLayout;

@end
24 changes: 23 additions & 1 deletion Source/ASPagerNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
//

#import <AsyncDisplayKit/ASPagerNode.h>
#import <AsyncDisplayKit/ASPagerNode+Beta.h>

#import <AsyncDisplayKit/ASCollectionGalleryLayoutDelegate.h>
#import <AsyncDisplayKit/ASCollectionNode+Beta.h>
#import <AsyncDisplayKit/ASDelegateProxy.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
Expand All @@ -25,7 +29,7 @@
#import <AsyncDisplayKit/ASCollectionView+Undeprecated.h>
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>

@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionDelegateFlowLayout, ASDelegateProxyInterceptor>
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionDelegate, ASCollectionDelegateFlowLayout, ASDelegateProxyInterceptor, ASCollectionGalleryLayoutSizeProviding>
{
__weak id <ASPagerDataSource> _pagerDataSource;
ASPagerNodeProxy *_proxyDataSource;
Expand Down Expand Up @@ -66,6 +70,16 @@ - (instancetype)initWithCollectionViewLayout:(ASPagerFlowLayout *)flowLayout;
return self;
}

- (instancetype)initUsingAsyncCollectionLayout
{
ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionHorizontalDirections];
self = [super initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil];
if (self) {
layoutDelegate.sizeProvider = self;
}
return self;
}

#pragma mark - ASDisplayNode

- (void)didLoad
Expand Down Expand Up @@ -123,6 +137,14 @@ - (NSInteger)indexOfPageWithNode:(ASCellNode *)node
return indexPath.row;
}

#pragma mark - ASCollectionGalleryLayoutSizeProviding

- (CGSize)sizeForElements:(ASElementMap *)elements
{
ASDisplayNodeAssertMainThread();
return self.bounds.size;
}

#pragma mark - ASCollectionDataSource

- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
Expand Down
6 changes: 3 additions & 3 deletions Source/Details/ASCollectionFlowLayoutDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,13 @@ - (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirect

- (ASScrollDirection)scrollableDirections
{
ASDisplayNodeAssertMainThread();
return _scrollableDirections;
}

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

Expand All @@ -59,9 +61,7 @@ + (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutConte
ASElementMap *elements = context.elements;
NSMutableArray<ASCellNode *> *children = ASArrayByFlatMapping(elements.itemElements, ASCollectionElement *element, element.node);
if (children.count == 0) {
return [[ASCollectionLayoutState alloc] initWithContext:context
contentSize:CGSizeZero
elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]];
return [[ASCollectionLayoutState alloc] initWithContext:context];
}

ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
Expand Down
21 changes: 20 additions & 1 deletion Source/Details/ASCollectionGalleryLayoutDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,25 @@
#import <AsyncDisplayKit/ASCollectionLayoutDelegate.h>
#import <AsyncDisplayKit/ASScrollDirection.h>

@class ASElementMap;

NS_ASSUME_NONNULL_BEGIN

@protocol ASCollectionGalleryLayoutSizeProviding <NSObject>

/**
* Returns the fixed size of each and every element.
*
* @discussion This method will only be called on main thread.
*
* @param elements All elements to be sized.
*
* @return The elements' size
*/
- (CGSize)sizeForElements:(ASElementMap *)elements;

@end

/**
* A thread-safe layout delegate that arranges items with the same size into a flow layout.
*
Expand All @@ -23,7 +40,9 @@ NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASCollectionGalleryLayoutDelegate : NSObject <ASCollectionLayoutDelegate>

- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections itemSize:(CGSize)itemSize NS_DESIGNATED_INITIALIZER;
@property (nonatomic, weak) id<ASCollectionGalleryLayoutSizeProviding> sizeProvider;

- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections NS_DESIGNATED_INITIALIZER;

- (instancetype)init __unavailable;

Expand Down
27 changes: 17 additions & 10 deletions Source/Details/ASCollectionGalleryLayoutDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,40 +31,47 @@ @implementation ASCollectionGalleryLayoutDelegate {
CGSize _itemSize;
}

- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections itemSize:(CGSize)itemSize
- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections
{
self = [super init];
if (self) {
ASDisplayNodeAssertFalse(CGSizeEqualToSize(CGSizeZero, itemSize));
_scrollableDirections = scrollableDirections;
_itemSize = itemSize;
}
return self;
}

- (ASScrollDirection)scrollableDirections
{
ASDisplayNodeAssertMainThread();
return _scrollableDirections;
}

- (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements
{
return [NSValue valueWithCGSize:_itemSize];
ASDisplayNodeAssertMainThread();
if (_sizeProvider == nil) {
return nil;
}

return [NSValue valueWithCGSize:[_sizeProvider sizeForElements:elements]];
}

+ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context
{
ASElementMap *elements = context.elements;
CGSize pageSize = context.viewportSize;
CGSize itemSize = ((NSValue *)context.additionalInfo).CGSizeValue;
ASScrollDirection scrollableDirections = context.scrollableDirections;

CGSize itemSize = context.additionalInfo ? ((NSValue *)context.additionalInfo).CGSizeValue : CGSizeZero;
if (CGSizeEqualToSize(CGSizeZero, itemSize)) {
return [[ASCollectionLayoutState alloc] initWithContext:context];
}

NSMutableArray<_ASGalleryLayoutItem *> *children = ASArrayByFlatMapping(elements.itemElements,
ASCollectionElement *element,
[[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]);
ASCollectionElement *element,
[[_ASGalleryLayoutItem alloc] initWithItemSize:itemSize collectionElement:element]);
if (children.count == 0) {
return [[ASCollectionLayoutState alloc] initWithContext:context
contentSize:CGSizeZero
elementToLayoutAttributesTable:[NSMapTable weakToStrongObjectsMapTable]];
return [[ASCollectionLayoutState alloc] initWithContext:context];
}

// Use a stack spec to calculate layout content size and frames of all elements without actually measuring each element
Expand Down
1 change: 0 additions & 1 deletion Source/Details/ASCollectionLayoutContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ - (instancetype)initWithViewportSize:(CGSize)viewportSize
{
self = [super init];
if (self) {
ASDisplayNodeAssertTrue([layoutDelegateClass conformsToProtocol:@protocol(ASCollectionLayoutDelegate)]);
_viewportSize = viewportSize;
_scrollableDirections = scrollableDirections;
_elements = elements;
Expand Down
2 changes: 2 additions & 0 deletions Source/Details/ASCollectionLayoutDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ NS_ASSUME_NONNULL_BEGIN
* It will be available in the context parameter in +calculateLayoutWithContext:
*
* @return The scrollable directions.
*
* @discusstion This method will be called on main thread.
*/
- (ASScrollDirection)scrollableDirections;

Expand Down
7 changes: 7 additions & 0 deletions Source/Details/ASCollectionLayoutState.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ AS_SUBCLASSING_RESTRICTED
contentSize:(CGSize)contentSize
elementToLayoutAttributesTable:(NSMapTable<ASCollectionElement *, UICollectionViewLayoutAttributes *> *)table NS_DESIGNATED_INITIALIZER;

/**
* Convenience initializer. Returns an object with zero content size and an empty table.
*
* @param context The context used to calculate this object
*/
- (instancetype)initWithContext:(ASCollectionLayoutContext *)context;

/**
* Convenience initializer.
*
Expand Down
7 changes: 7 additions & 0 deletions Source/Details/ASCollectionLayoutState.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ @implementation ASCollectionLayoutState {
ASPageToLayoutAttributesTable *_unmeasuredPageToLayoutAttributesTable;
}

- (instancetype)initWithContext:(ASCollectionLayoutContext *)context
{
return [self initWithContext:context
contentSize:CGSizeZero
elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]];
}

- (instancetype)initWithContext:(ASCollectionLayoutContext *)context
layout:(ASLayout *)layout
getElementBlock:(ASCollectionElement *(^)(ASLayout *))getElementBlock
Expand Down
5 changes: 1 addition & 4 deletions Source/Private/ASCollectionLayout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,9 @@ - (ASCollectionLayoutContext *)layoutContextWithElements:(ASElementMap *)element
+ (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutContext *)context
{
if (context.elements == nil) {
return [[ASCollectionLayoutState alloc] initWithContext:context
contentSize:CGSizeZero
elementToLayoutAttributesTable:[NSMapTable elementToLayoutAttributesTable]];
return [[ASCollectionLayoutState alloc] initWithContext:context];
}

ASDisplayNodeAssertTrue([context.layoutDelegateClass conformsToProtocol:@protocol(ASCollectionLayoutDelegate)]);
ASCollectionLayoutState *layout = [context.layoutDelegateClass calculateLayoutWithContext:context];
[context.layoutCache setLayout:layout forContext:context];

Expand Down
14 changes: 11 additions & 3 deletions examples/ASCollectionView/Sample/ViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

#define ASYNC_COLLECTION_LAYOUT 0

@interface ViewController () <ASCollectionDataSource, ASCollectionDelegateFlowLayout>
@interface ViewController () <ASCollectionDataSource, ASCollectionDelegateFlowLayout, ASCollectionGalleryLayoutSizeProviding>

@property (nonatomic, strong) ASCollectionNode *collectionNode;
@property (nonatomic, strong) NSArray *data;
Expand All @@ -47,8 +47,8 @@ - (void)viewDidLoad
[super viewDidLoad];

#if ASYNC_COLLECTION_LAYOUT
id<ASCollectionLayoutDelegate> layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections
itemSize:CGSizeMake(180, 90)];
ASCollectionGalleryLayoutDelegate *layoutDelegate = [[ASCollectionGalleryLayoutDelegate alloc] initWithScrollableDirections:ASScrollDirectionVerticalDirections];
layoutDelegate.sizeProvider = self;
self.collectionNode = [[ASCollectionNode alloc] initWithLayoutDelegate:layoutDelegate layoutFacilitator:nil];
#else
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
Expand Down Expand Up @@ -108,6 +108,14 @@ - (void)reloadTapped
[self.collectionNode reloadData];
}

#pragma mark - ASCollectionGalleryLayoutSizeProviding

- (CGSize)sizeForElements:(ASElementMap *)elements
{
ASDisplayNodeAssertMainThread();
return CGSizeMake(180, 90);
}

#pragma mark - ASCollectionView Data Source

- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ - (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight:

- (ASScrollDirection)scrollableDirections
{
ASDisplayNodeAssertMainThread();
return ASScrollDirectionVerticalDirections;
}

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

Expand Down

0 comments on commit 78c133e

Please sign in to comment.