diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index edcbb825e..ba9df64e5 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -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 */; }; @@ -912,6 +913,7 @@ E51B78BD1F01A0EE00E32604 /* ASLayoutFlatteningTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutFlatteningTests.m; sourceTree = ""; }; E52405B21C8FEF03004DC8E7 /* ASLayoutTransition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutTransition.mm; sourceTree = ""; }; E52405B41C8FEF16004DC8E7 /* ASLayoutTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutTransition.h; sourceTree = ""; }; + E54E00711F1D3828000B30D7 /* ASPagerNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASPagerNode+Beta.h"; sourceTree = ""; }; E54E81FA1EB357BD00FFE8E1 /* ASPageTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPageTable.h; sourceTree = ""; }; E54E81FB1EB357BD00FFE8E1 /* ASPageTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPageTable.m; sourceTree = ""; }; E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutElement.mm; sourceTree = ""; }; @@ -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 */, @@ -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 */, diff --git a/CHANGELOG.md b/CHANGELOG.md index a24bc323f..d9bb2dedd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/Source/ASPagerNode+Beta.h b/Source/ASPagerNode+Beta.h new file mode 100644 index 000000000..bd963ec19 --- /dev/null +++ b/Source/ASPagerNode+Beta.h @@ -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 + +@interface ASPagerNode (Beta) + +- (instancetype)initUsingAsyncCollectionLayout; + +@end diff --git a/Source/ASPagerNode.m b/Source/ASPagerNode.m index e144090aa..5b95fec2e 100644 --- a/Source/ASPagerNode.m +++ b/Source/ASPagerNode.m @@ -16,6 +16,10 @@ // #import +#import + +#import +#import #import #import #import @@ -25,7 +29,7 @@ #import #import -@interface ASPagerNode () +@interface ASPagerNode () { ASPagerFlowLayout *_flowLayout; @@ -71,6 +75,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 @@ -128,6 +142,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 diff --git a/Source/Details/ASCollectionFlowLayoutDelegate.m b/Source/Details/ASCollectionFlowLayoutDelegate.m index 855f3791c..79287a594 100644 --- a/Source/Details/ASCollectionFlowLayoutDelegate.m +++ b/Source/Details/ASCollectionFlowLayoutDelegate.m @@ -46,11 +46,13 @@ - (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirect - (ASScrollDirection)scrollableDirections { + ASDisplayNodeAssertMainThread(); return _scrollableDirections; } - (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements { + ASDisplayNodeAssertMainThread(); return nil; } @@ -59,9 +61,7 @@ + (ASCollectionLayoutState *)calculateLayoutWithContext:(ASCollectionLayoutConte ASElementMap *elements = context.elements; NSMutableArray *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 diff --git a/Source/Details/ASCollectionGalleryLayoutDelegate.h b/Source/Details/ASCollectionGalleryLayoutDelegate.h index 2b9e64c7c..d2d41b788 100644 --- a/Source/Details/ASCollectionGalleryLayoutDelegate.h +++ b/Source/Details/ASCollectionGalleryLayoutDelegate.h @@ -13,8 +13,25 @@ #import #import +@class ASElementMap; + NS_ASSUME_NONNULL_BEGIN +@protocol ASCollectionGalleryLayoutSizeProviding + +/** + * 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. * @@ -23,7 +40,9 @@ NS_ASSUME_NONNULL_BEGIN AS_SUBCLASSING_RESTRICTED @interface ASCollectionGalleryLayoutDelegate : NSObject -- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections itemSize:(CGSize)itemSize NS_DESIGNATED_INITIALIZER; +@property (nonatomic, weak) id sizeProvider; + +- (instancetype)initWithScrollableDirections:(ASScrollDirection)scrollableDirections NS_DESIGNATED_INITIALIZER; - (instancetype)init __unavailable; diff --git a/Source/Details/ASCollectionGalleryLayoutDelegate.m b/Source/Details/ASCollectionGalleryLayoutDelegate.m index 0c98f0fbf..5ab5c23d2 100644 --- a/Source/Details/ASCollectionGalleryLayoutDelegate.m +++ b/Source/Details/ASCollectionGalleryLayoutDelegate.m @@ -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 diff --git a/Source/Details/ASCollectionLayoutContext.m b/Source/Details/ASCollectionLayoutContext.m index 6620e391e..eff48afb6 100644 --- a/Source/Details/ASCollectionLayoutContext.m +++ b/Source/Details/ASCollectionLayoutContext.m @@ -37,7 +37,6 @@ - (instancetype)initWithViewportSize:(CGSize)viewportSize { self = [super init]; if (self) { - ASDisplayNodeAssertTrue([layoutDelegateClass conformsToProtocol:@protocol(ASCollectionLayoutDelegate)]); _viewportSize = viewportSize; _scrollableDirections = scrollableDirections; _elements = elements; diff --git a/Source/Details/ASCollectionLayoutDelegate.h b/Source/Details/ASCollectionLayoutDelegate.h index 9ef3da874..4e75b25fc 100644 --- a/Source/Details/ASCollectionLayoutDelegate.h +++ b/Source/Details/ASCollectionLayoutDelegate.h @@ -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; diff --git a/Source/Details/ASCollectionLayoutState.h b/Source/Details/ASCollectionLayoutState.h index 1804c58a3..c21a37dff 100644 --- a/Source/Details/ASCollectionLayoutState.h +++ b/Source/Details/ASCollectionLayoutState.h @@ -56,6 +56,13 @@ AS_SUBCLASSING_RESTRICTED contentSize:(CGSize)contentSize elementToLayoutAttributesTable:(NSMapTable *)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. * diff --git a/Source/Details/ASCollectionLayoutState.mm b/Source/Details/ASCollectionLayoutState.mm index 0b2616a22..81f003ab4 100644 --- a/Source/Details/ASCollectionLayoutState.mm +++ b/Source/Details/ASCollectionLayoutState.mm @@ -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 diff --git a/Source/Private/ASCollectionLayout.mm b/Source/Private/ASCollectionLayout.mm index 935a3d680..2ecc312c7 100644 --- a/Source/Private/ASCollectionLayout.mm +++ b/Source/Private/ASCollectionLayout.mm @@ -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]; diff --git a/examples/ASCollectionView/Sample/ViewController.m b/examples/ASCollectionView/Sample/ViewController.m index 13bfe64c7..36391e8f3 100644 --- a/examples/ASCollectionView/Sample/ViewController.m +++ b/examples/ASCollectionView/Sample/ViewController.m @@ -23,7 +23,7 @@ #define ASYNC_COLLECTION_LAYOUT 0 -@interface ViewController () +@interface ViewController () @property (nonatomic, strong) ASCollectionNode *collectionNode; @property (nonatomic, strong) NSArray *data; @@ -47,8 +47,8 @@ - (void)viewDidLoad [super viewDidLoad]; #if ASYNC_COLLECTION_LAYOUT - id 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]; @@ -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; diff --git a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m index a7383f294..196925c91 100644 --- a/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m +++ b/examples/CustomCollectionView/Sample/MosaicCollectionLayoutDelegate.m @@ -36,11 +36,13 @@ - (instancetype)initWithNumberOfColumns:(NSInteger)numberOfColumns headerHeight: - (ASScrollDirection)scrollableDirections { + ASDisplayNodeAssertMainThread(); return ASScrollDirectionVerticalDirections; } - (id)additionalInfoForLayoutWithElements:(ASElementMap *)elements { + ASDisplayNodeAssertMainThread(); return _info; }