diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSection.m b/ASDKListKit/ASDKListKitTests/ASListTestSection.m index 239819b14f..eac14aec0f 100644 --- a/ASDKListKit/ASDKListKitTests/ASListTestSection.m +++ b/ASDKListKit/ASDKListKitTests/ASListTestSection.m @@ -27,11 +27,13 @@ - (CGSize)sizeForItemAtIndex:(NSInteger)index { - ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); - return CGSizeMake(100, 10); + return [ASIGListSectionControllerMethods sizeForItemAtIndex:index]; } -ASIGSectionControllerCellForIndexImplementation +- (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index +{ + return [ASIGListSectionControllerMethods cellForItemAtIndex:index sectionController:self]; +} - (void)didUpdateToObject:(id)object { diff --git a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m b/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m index 6197d2c4c8..2184258a53 100644 --- a/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m +++ b/ASDKListKit/ASDKListKitTests/ASListTestSupplementarySource.m @@ -11,14 +11,23 @@ @implementation ASListTestSupplementarySource -ASIGSupplementarySourceViewForSupplementaryElementImplementation(self.sectionController) -ASIGSupplementarySourceSizeForSupplementaryElementImplementation - -- (ASCellNode *)nodeForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index +- (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index { - ASListTestSupplementaryNode *node = [[ASListTestSupplementaryNode alloc] init]; - node.style.preferredSize = CGSizeMake(100, 10); - return node; + return [ASIGListSupplementaryViewSourceMethods viewForSupplementaryElementOfKind:elementKind atIndex:index sectionController:self.sectionController]; +} + +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + return [ASIGListSupplementaryViewSourceMethods sizeForSupplementaryViewOfKind:elementKind atIndex:index]; +} + +- (ASCellNodeBlock)nodeBlockForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + return ^{ + ASListTestSupplementaryNode *node = [[ASListTestSupplementaryNode alloc] init]; + node.style.preferredSize = CGSizeMake(100, 10); + return node; + }; } @end diff --git a/AsyncDisplayKit.podspec b/AsyncDisplayKit.podspec index 1b688c75ae..ff026649f1 100644 --- a/AsyncDisplayKit.podspec +++ b/AsyncDisplayKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'AsyncDisplayKit' - spec.version = '2.1' + spec.version = '2.2' spec.license = { :type => 'BSD' } spec.homepage = 'http://asyncdisplaykit.org' spec.authors = { 'Scott Goodson' => 'scottgoodson@gmail.com' } @@ -19,26 +19,26 @@ Pod::Spec.new do |spec| # Subspecs spec.subspec 'Core' do |core| - core.prefix_header_file = 'AsyncDisplayKit/AsyncDisplayKit-Prefix.pch' + core.prefix_header_file = 'Source/AsyncDisplayKit-Prefix.pch' core.public_header_files = [ - 'AsyncDisplayKit/*.h', - 'AsyncDisplayKit/Details/**/*.h', - 'AsyncDisplayKit/Layout/**/*.h', - 'Base/*.h', - 'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h', - 'AsyncDisplayKit/TextKit/ASTextNodeTypes.h', - 'AsyncDisplayKit/TextKit/ASTextKitComponents.h' + 'Source/*.h', + 'Source/Details/**/*.h', + 'Source/Layout/**/*.h', + 'Source/Base/*.h', + 'Source/Debug/AsyncDisplayKit+Debug.h', + 'Source/TextKit/ASTextNodeTypes.h', + 'Source/TextKit/ASTextKitComponents.h' ] core.source_files = [ - 'AsyncDisplayKit/**/*.{h,m,mm}', + 'Source/**/*.{h,m,mm}', 'Base/*.{h,m}', # Most TextKit components are not public because the C++ content # in the headers will cause build errors when using # `use_frameworks!` on 0.39.0 & Swift 2.1. # See https://github.com/facebook/AsyncDisplayKit/issues/1153 - 'AsyncDisplayKit/TextKit/*.h', + 'Source/TextKit/*.h', ] core.xcconfig = { 'GCC_PRECOMPILE_PREFIX_HEADER' => 'YES' } end @@ -46,7 +46,7 @@ Pod::Spec.new do |spec| spec.subspec 'PINRemoteImage' do |pin| # Note: The core.prefix_header_file includes setup of PIN_REMOTE_IMAGE, so the line below could be removed. pin.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) PIN_REMOTE_IMAGE=1' } - pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.7' + pin.dependency 'PINRemoteImage/iOS', '= 3.0.0-beta.8' pin.dependency 'PINRemoteImage/PINCache' pin.dependency 'AsyncDisplayKit/Core' end diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index 5f318d8b3c..0323e441f3 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -17,14 +17,14 @@ 052EE06B1A15A0D8002C6279 /* TestResources in Resources */ = {isa = PBXBuildFile; fileRef = 052EE06A1A15A0D8002C6279 /* TestResources */; }; 056D21551ABCEF50001107EF /* ASImageNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 056D21541ABCEF50001107EF /* ASImageNodeSnapshotTests.m */; }; 057D02C41AC0A66700C7AC3C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 057D02C31AC0A66700C7AC3C /* main.m */; }; - 057D02C71AC0A66700C7AC3C /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 057D02C61AC0A66700C7AC3C /* AppDelegate.mm */; }; + 057D02C71AC0A66700C7AC3C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 057D02C61AC0A66700C7AC3C /* AppDelegate.m */; }; 058D09BE195D04C000B7D73C /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09BD195D04C000B7D73C /* XCTest.framework */; }; 058D09BF195D04C000B7D73C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AF195D04C000B7D73C /* Foundation.framework */; }; 058D09C1195D04C000B7D73C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09C0195D04C000B7D73C /* UIKit.framework */; }; 058D09CA195D04C000B7D73C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 058D09C8195D04C000B7D73C /* InfoPlist.strings */; }; 058D0A38195D057000B7D73C /* ASDisplayLayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A2D195D057000B7D73C /* ASDisplayLayerTests.m */; }; 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A2E195D057000B7D73C /* ASDisplayNodeAppearanceTests.m */; }; - 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.m */; }; + 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.mm */; }; 058D0A3B195D057000B7D73C /* ASDisplayNodeTestsHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A31195D057000B7D73C /* ASDisplayNodeTestsHelper.m */; }; 058D0A3C195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A32195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m */; }; 058D0A3D195D057000B7D73C /* ASTextKitCoreTextAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D0A33195D057000B7D73C /* ASTextKitCoreTextAdditionsTests.m */; }; @@ -33,7 +33,6 @@ 05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 05EA6FE61AC0966E00E35788 /* ASSnapshotTestCase.m */; }; 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */; }; - 204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 204C979D1B362CB3002B1083 /* Default-568h@2x.png */; }; 242995D31B29743C00090100 /* ASBasicImageDownloaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */; }; 2538B6F31BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */; }; 254C6B521BF8FE6D003EC431 /* ASTextKitTruncationTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */; }; @@ -96,9 +95,9 @@ 34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = ACF6ED191B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm */; }; 3C9C128519E616EF00E942A0 /* ASTableViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C9C128419E616EF00E942A0 /* ASTableViewTests.mm */; }; 509E68601B3AED8E009B9150 /* ASScrollDirection.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E111B371BD7007741D0 /* ASScrollDirection.m */; }; - 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 509E68611B3AEDA0009B9150 /* ASAbstractLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E171B37339C007741D0 /* ASAbstractLayoutController.h */; }; 509E68621B3AEDA5009B9150 /* ASAbstractLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E181B37339C007741D0 /* ASAbstractLayoutController.mm */; }; - 509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 509E68631B3AEDB4009B9150 /* ASCollectionViewLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */; }; 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */; }; 509E68651B3AEDC5009B9150 /* CoreGraphics+ASConvenience.h in Headers */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; settings = {ATTRIBUTES = (Public, ); }; }; 509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */; }; @@ -181,7 +180,6 @@ 92DD2FE61BF4D05E0074C9DD /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; }; 92DD2FE81BF4D0A80074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */; }; 9C49C3701B853961000B0DD5 /* ASStackLayoutElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C55866B1BD54A1900B50E3A /* ASAsciiArtBoxCreator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */; }; 9C55866C1BD54A3000B50E3A /* ASAsciiArtBoxCreator.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -190,8 +188,6 @@ 9C70F2061CDA4F0C007D6C76 /* ASTraitCollection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C70F2011CDA4EFA007D6C76 /* ASTraitCollection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */; }; 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */; }; - 9C70F20B1CDBE9A4007D6C76 /* ASDataController+Subclasses.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */; settings = {ATTRIBUTES = (Private, ); }; }; - 9C70F20C1CDBE9B6007D6C76 /* ASCollectionDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */ = {isa = PBXBuildFile; fileRef = AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */; settings = {ATTRIBUTES = (Private, ); }; }; 9C70F20E1CDBE9E5007D6C76 /* NSArray+Diffing.h in Headers */ = {isa = PBXBuildFile; fileRef = DBC452D91C5BF64600B16017 /* NSArray+Diffing.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C70F20F1CDBE9FF007D6C76 /* ASLayoutManager.h in Headers */ = {isa = PBXBuildFile; fileRef = B30BF6501C5964B0004FCD53 /* ASLayoutManager.h */; settings = {ATTRIBUTES = (Private, ); }; }; @@ -266,11 +262,11 @@ B350621D1B010EFD0018CF92 /* ASHighlightOverlayLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E7195D050800B7D73C /* ASHighlightOverlayLayer.mm */; }; B350621F1B010EFD0018CF92 /* ASImageProtocols.h in Headers */ = {isa = PBXBuildFile; fileRef = 05F20AA31A15733C00DCA68A /* ASImageProtocols.h */; settings = {ATTRIBUTES = (Public, ); }; }; - B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521D1A3F83C40061C0BA /* ASLayoutController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4640521D1A3F83C40061C0BA /* ASLayoutController.h */; }; B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */ = {isa = PBXBuildFile; fileRef = 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35062241B010EFD0018CF92 /* ASMutableAttributedStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D09E8195D050800B7D73C /* ASMutableAttributedStringBuilder.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35062251B010EFD0018CF92 /* ASMutableAttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = 058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */; }; - B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3619ABD413004DAFF1 /* ASRangeController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B35062261B010EFD0018CF92 /* ASRangeController.h in Headers */ = {isa = PBXBuildFile; fileRef = 055F1A3619ABD413004DAFF1 /* ASRangeController.h */; }; B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */; }; B350622D1B010EFD0018CF92 /* ASScrollDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 296A0A311A951715005ACEAA /* ASScrollDirection.h */; settings = {ATTRIBUTES = (Public, ); }; }; B35062391B010EFD0018CF92 /* ASThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 058D0A12195D050800B7D73C /* ASThread.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -304,6 +300,13 @@ B350625D1B0111740018CF92 /* Photos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943141A1575670030A7D0 /* Photos.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; B350625E1B0111780018CF92 /* AssetsLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 051943121A1575630030A7D0 /* AssetsLibrary.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; C78F7E2B1BF7809800CDEAFC /* ASTableNode.h in Headers */ = {isa = PBXBuildFile; fileRef = B0F880581BEAEC7500D17647 /* ASTableNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC034A011E5FAF9700626263 /* ASElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC0349FF1E5FAF9700626263 /* ASElementMap.h */; }; + CC034A021E5FAF9700626263 /* ASElementMap.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A001E5FAF9700626263 /* ASElementMap.m */; }; + CC034A0D1E60C3D500626263 /* ASRectTable.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A0B1E60C3D500626263 /* ASRectTable.h */; }; + CC034A0E1E60C3D500626263 /* ASRectTable.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A0C1E60C3D500626263 /* ASRectTable.m */; }; + CC034A101E60C9BF00626263 /* ASRectTableTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */; }; + CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */; }; + CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = CC034A121E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m */; }; CC051F1F1D7A286A006434CB /* ASCALayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC051F1E1D7A286A006434CB /* ASCALayerTests.m */; }; CC0AEEA41D66316E005D1C78 /* ASUICollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */; }; CC0F885B1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m in Sources */ = {isa = PBXBuildFile; fileRef = CC0F88591E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m */; }; @@ -312,10 +315,11 @@ CC0F88601E4280B800576FED /* _ASCollectionViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */; settings = {ATTRIBUTES = (Private, ); }; }; CC0F88621E4281E200576FED /* ASSectionController.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE04B1E1E313EA7006AEBBB /* ASSectionController.h */; settings = {ATTRIBUTES = (Public, ); }; }; CC0F88631E4281E700576FED /* ASSupplementaryNodeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = CCE04B2B1E314A32006AEBBB /* ASSupplementaryNodeSource.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CC0F886B1E4286FA00576FED /* ReferenceImages_32 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F88681E4286FA00576FED /* ReferenceImages_32 */; }; CC0F886C1E4286FA00576FED /* ReferenceImages_64 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F88691E4286FA00576FED /* ReferenceImages_64 */; }; CC0F886D1E4286FA00576FED /* ReferenceImages_iOS_10 in Resources */ = {isa = PBXBuildFile; fileRef = CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */; }; CC11F97A1DB181180024D77B /* ASNetworkImageNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */; }; + CC2F65EE1E5FFB1600DA57C9 /* ASMutableElementMap.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */; }; + CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */ = {isa = PBXBuildFile; fileRef = CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */; }; CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20811C3F76D600798563 /* ASPendingStateController.h */; settings = {ATTRIBUTES = (Private, ); }; }; CC3B20861C3F76D600798563 /* ASPendingStateController.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20821C3F76D600798563 /* ASPendingStateController.mm */; }; CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */ = {isa = PBXBuildFile; fileRef = CC3B20871C3F7A5400798563 /* ASWeakSet.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -327,6 +331,10 @@ CC4C2A791D88E3BF0039ACAB /* ASTraceEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = CC4C2A761D88E3BF0039ACAB /* ASTraceEvent.m */; }; CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; settings = {ATTRIBUTES = (Private, ); }; }; CC54A81E1D7008B300296A24 /* ASDispatchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.m */; }; + CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */; }; + CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */; }; + CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */; }; CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; }; CC57EAF81E3939450034C595 /* ASTableView+Undeprecated.h in Headers */ = {isa = PBXBuildFile; fileRef = CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */; settings = {ATTRIBUTES = (Private, ); }; }; CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -361,8 +369,8 @@ DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DEFAD8131CC48914000527C4 /* ASVideoNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = AEEC47E01C20C2DD00EC1693 /* ASVideoNode.mm */; }; E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */; }; - E5711A2C1C840C81009619D4 /* ASIndexedNodeContext.h in Headers */ = {isa = PBXBuildFile; fileRef = E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */; settings = {ATTRIBUTES = (Private, ); }; }; - E5711A301C840C96009619D4 /* ASIndexedNodeContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5711A2D1C840C96009619D4 /* ASIndexedNodeContext.mm */; }; + E5711A2C1C840C81009619D4 /* ASCollectionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = E5711A2A1C840C81009619D4 /* ASCollectionElement.h */; settings = {ATTRIBUTES = (Private, ); }; }; + E5711A301C840C96009619D4 /* ASCollectionElement.mm in Sources */ = {isa = PBXBuildFile; fileRef = E5711A2D1C840C96009619D4 /* ASCollectionElement.mm */; }; F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F711994D1D20C21100568860 /* ASDisplayNodeExtrasTests.m */; }; /* End PBXBuildFile section */ @@ -406,7 +414,7 @@ 057D02C21AC0A66700C7AC3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 057D02C31AC0A66700C7AC3C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 057D02C51AC0A66700C7AC3C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 057D02C61AC0A66700C7AC3C /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = AppDelegate.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + 057D02C61AC0A66700C7AC3C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 0587F9BB1A7309ED00AFF0BA /* ASEditableTextNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEditableTextNode.h; sourceTree = ""; }; 0587F9BC1A7309ED00AFF0BA /* ASEditableTextNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASEditableTextNode.mm; sourceTree = ""; }; 058D09AF195D04C000B7D73C /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -460,7 +468,7 @@ 058D0A12195D050800B7D73C /* ASThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASThread.h; sourceTree = ""; }; 058D0A2D195D057000B7D73C /* ASDisplayLayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDisplayLayerTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 058D0A2E195D057000B7D73C /* ASDisplayNodeAppearanceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDisplayNodeAppearanceTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeTests.m; sourceTree = ""; }; + 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeTests.mm; sourceTree = ""; }; 058D0A30195D057000B7D73C /* ASDisplayNodeTestsHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeTestsHelper.h; sourceTree = ""; }; 058D0A31195D057000B7D73C /* ASDisplayNodeTestsHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeTestsHelper.m; sourceTree = ""; }; 058D0A32195D057000B7D73C /* ASMutableAttributedStringBuilderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASMutableAttributedStringBuilderTests.m; sourceTree = ""; }; @@ -474,7 +482,6 @@ 18C2ED7C1B9B7DE800F627B3 /* ASCollectionNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionNode.h; sourceTree = ""; }; 18C2ED7D1B9B7DE800F627B3 /* ASCollectionNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionNode.mm; sourceTree = ""; }; 1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEqualityHelpers.h; sourceTree = ""; }; - 204C979D1B362CB3002B1083 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; 205F0E0D1B371875007741D0 /* UICollectionViewLayout+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UICollectionViewLayout+ASConvenience.h"; sourceTree = ""; }; 205F0E0E1B371875007741D0 /* UICollectionViewLayout+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UICollectionViewLayout+ASConvenience.m"; sourceTree = ""; }; 205F0E111B371BD7007741D0 /* ASScrollDirection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASScrollDirection.m; sourceTree = ""; }; @@ -485,9 +492,6 @@ 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CoreGraphics+ASConvenience.h"; sourceTree = ""; }; 205F0E201B376416007741D0 /* CoreGraphics+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CoreGraphics+ASConvenience.m"; sourceTree = ""; }; 242995D21B29743C00090100 /* ASBasicImageDownloaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASBasicImageDownloaderTests.m; sourceTree = ""; }; - 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASCollectionDataController.h; sourceTree = ""; }; - 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionDataController.mm; sourceTree = ""; }; - 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDataController+Subclasses.h"; sourceTree = ""; }; 2538B6F21BC5D2A2003CA0B4 /* ASCollectionViewFlowLayoutInspectorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASCollectionViewFlowLayoutInspectorTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 254C6B511BF8FE6D003EC431 /* ASTextKitTruncationTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTruncationTests.mm; sourceTree = ""; }; 254C6B531BF8FF2A003EC431 /* ASTextKitTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTextKitTests.mm; sourceTree = ""; }; @@ -520,7 +524,7 @@ 2911485B1A77147A005D0878 /* ASControlNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASControlNodeTests.m; sourceTree = ""; }; 292C59991A956527007E5DD6 /* ASLayoutRangeType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutRangeType.h; sourceTree = ""; }; 2967F9E11AB0A4CF0072E4AB /* ASBasicImageDownloaderInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASBasicImageDownloaderInternal.h; sourceTree = ""; }; - 296A0A311A951715005ACEAA /* ASScrollDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = AsyncDisplayKit/Details/ASScrollDirection.h; sourceTree = SOURCE_ROOT; }; + 296A0A311A951715005ACEAA /* ASScrollDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASScrollDirection.h; path = Source/Details/ASScrollDirection.h; sourceTree = SOURCE_ROOT; }; 296A0A341A951ABF005ACEAA /* ASBatchFetchingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASBatchFetchingTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 299DA1A71A828D2900162D41 /* ASBatchContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBatchContext.h; sourceTree = ""; }; 299DA1A81A828D2900162D41 /* ASBatchContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBatchContext.mm; sourceTree = ""; }; @@ -553,10 +557,10 @@ 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASVisibilityProtocols.m; sourceTree = ""; }; 6907C2561DC4ECFE00374C66 /* ASObjectDescriptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASObjectDescriptionHelpers.h; sourceTree = ""; }; 6907C2571DC4ECFE00374C66 /* ASObjectDescriptionHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASObjectDescriptionHelpers.m; sourceTree = ""; }; - 690C35601E055C5D00069B91 /* ASDimensionInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimensionInternal.mm; path = AsyncDisplayKit/Layout/ASDimensionInternal.mm; sourceTree = ""; }; - 690C35631E055C7B00069B91 /* ASDimensionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimensionInternal.h; path = AsyncDisplayKit/Layout/ASDimensionInternal.h; sourceTree = ""; }; - 690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimensionDeprecated.mm; path = AsyncDisplayKit/Layout/ASDimensionDeprecated.mm; sourceTree = ""; }; - 690C356A1E05680300069B91 /* ASDimensionDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimensionDeprecated.h; path = AsyncDisplayKit/Layout/ASDimensionDeprecated.h; sourceTree = ""; }; + 690C35601E055C5D00069B91 /* ASDimensionInternal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimensionInternal.mm; sourceTree = ""; }; + 690C35631E055C7B00069B91 /* ASDimensionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDimensionInternal.h; sourceTree = ""; }; + 690C35651E0567C600069B91 /* ASDimensionDeprecated.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimensionDeprecated.mm; sourceTree = ""; }; + 690C356A1E05680300069B91 /* ASDimensionDeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDimensionDeprecated.h; sourceTree = ""; }; 690ED58D1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementStylePrivate.h; sourceTree = ""; }; 690ED5921E36D118000627C0 /* ASControlNode+tvOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASControlNode+tvOS.h"; sourceTree = ""; }; 690ED5931E36D118000627C0 /* ASControlNode+tvOS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASControlNode+tvOS.m"; sourceTree = ""; }; @@ -570,17 +574,17 @@ 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = ""; }; 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = ""; }; 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWrapperSpecSnapshotTests.mm; sourceTree = ""; }; - 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEventLog.h; path = AsyncDisplayKit/Details/ASEventLog.h; sourceTree = SOURCE_ROOT; }; - 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEventLog.mm; path = AsyncDisplayKit/Details/ASEventLog.mm; sourceTree = SOURCE_ROOT; }; + 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASEventLog.h; sourceTree = ""; }; + 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASEventLog.mm; sourceTree = ""; }; 696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASBackgroundLayoutSpecSnapshotTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = ""; }; 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = ""; }; - 6977965D1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASLayoutSpec+Subclasses.h"; path = "AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h"; sourceTree = ""; }; - 6977965E1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "ASLayoutSpec+Subclasses.mm"; path = "AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm"; sourceTree = ""; }; + 6977965D1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASLayoutSpec+Subclasses.h"; sourceTree = ""; }; + 6977965E1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASLayoutSpec+Subclasses.mm"; sourceTree = ""; }; 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASEditableTextNodeTests.m; sourceTree = ""; }; 698371D91E4379CD00437585 /* ASNodeController+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASNodeController+Beta.h"; sourceTree = ""; }; 698371DA1E4379CD00437585 /* ASNodeController+Beta.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASNodeController+Beta.m"; sourceTree = ""; }; - 698C8B601CAB49FC0052DC3F /* ASLayoutElementExtensibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementExtensibility.h; path = AsyncDisplayKit/Layout/ASLayoutElementExtensibility.h; sourceTree = ""; }; + 698C8B601CAB49FC0052DC3F /* ASLayoutElementExtensibility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementExtensibility.h; sourceTree = ""; }; 698DFF431E36B6C9002891F1 /* ASStackLayoutSpecUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackLayoutSpecUtilities.h; sourceTree = ""; }; 698DFF461E36B7E9002891F1 /* ASLayoutSpecUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutSpecUtilities.h; sourceTree = ""; }; 699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutSpecTests.m; sourceTree = ""; }; @@ -591,10 +595,10 @@ 69F10C851C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASRangeControllerUpdateRangeProtocol+Beta.h"; sourceTree = ""; }; 69FEE53C1D95A9AF0086F066 /* ASLayoutElementStyleTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASLayoutElementStyleTests.m; sourceTree = ""; }; 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = ""; }; - 764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "AsyncDisplayKit+Debug.h"; path = "../AsyncDisplayKit+Debug.h"; sourceTree = ""; }; - 764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "AsyncDisplayKit+Debug.m"; path = "../AsyncDisplayKit+Debug.m"; sourceTree = ""; }; - 7A06A7381C35F08800FE8DAA /* ASRelativeLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASRelativeLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm; sourceTree = ""; }; - 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRelativeLayoutSpec.h; path = AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h; sourceTree = ""; }; + 764D83D21C8EA515009B4FB8 /* AsyncDisplayKit+Debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+Debug.h"; sourceTree = ""; }; + 764D83D31C8EA515009B4FB8 /* AsyncDisplayKit+Debug.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+Debug.m"; sourceTree = ""; }; + 7A06A7381C35F08800FE8DAA /* ASRelativeLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpec.mm; sourceTree = ""; }; + 7A06A7391C35F08800FE8DAA /* ASRelativeLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRelativeLayoutSpec.h; sourceTree = ""; }; 7AB338681C55B97B0055FDE8 /* ASRelativeLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRelativeLayoutSpecSnapshotTests.mm; sourceTree = ""; }; 8021EC1A1D2B00B100799119 /* UIImage+ASConvenience.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+ASConvenience.h"; sourceTree = ""; }; 8021EC1B1D2B00B100799119 /* UIImage+ASConvenience.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+ASConvenience.m"; sourceTree = ""; }; @@ -612,14 +616,14 @@ 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMapNode.h; sourceTree = ""; }; 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMapNode.mm; sourceTree = ""; }; 92DD2FE51BF4D05E0074C9DD /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; - 9C49C36E1B853957000B0DD5 /* ASStackLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutElement.h; path = AsyncDisplayKit/Layout/ASStackLayoutElement.h; sourceTree = ""; }; - 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAsciiArtBoxCreator.h; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h; sourceTree = ""; }; - 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASAsciiArtBoxCreator.m; path = AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m; sourceTree = ""; }; - 9C6BB3B01B8CC9C200F13F52 /* ASAbsoluteLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAbsoluteLayoutElement.h; path = AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h; sourceTree = ""; }; + 9C49C36E1B853957000B0DD5 /* ASStackLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackLayoutElement.h; sourceTree = ""; }; + 9C5586671BD549CB00B50E3A /* ASAsciiArtBoxCreator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASAsciiArtBoxCreator.h; sourceTree = ""; }; + 9C5586681BD549CB00B50E3A /* ASAsciiArtBoxCreator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASAsciiArtBoxCreator.m; sourceTree = ""; }; + 9C6BB3B01B8CC9C200F13F52 /* ASAbsoluteLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASAbsoluteLayoutElement.h; sourceTree = ""; }; 9C70F2011CDA4EFA007D6C76 /* ASTraitCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTraitCollection.h; sourceTree = ""; }; 9C70F2021CDA4EFA007D6C76 /* ASTraitCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTraitCollection.m; sourceTree = ""; }; 9C8898BA1C738B9800D6B02E /* ASTextKitFontSizeAdjuster.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASTextKitFontSizeAdjuster.mm; path = TextKit/ASTextKitFontSizeAdjuster.mm; sourceTree = ""; }; - 9CDC18CB1B910E12004965E2 /* ASLayoutElementPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutElementPrivate.h; path = AsyncDisplayKit/Layout/ASLayoutElementPrivate.h; sourceTree = ""; }; + 9CDC18CB1B910E12004965E2 /* ASLayoutElementPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementPrivate.h; sourceTree = ""; }; 9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASViewController.mm; sourceTree = ""; }; 9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTableNode.mm; sourceTree = ""; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionViewTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; @@ -632,7 +636,7 @@ AC026B571BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASAbsoluteLayoutSpecSnapshotTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASHierarchyChangeSet.h; sourceTree = ""; }; AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASHierarchyChangeSet.mm; sourceTree = ""; }; - AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = ""; }; + AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackLayoutDefines.h; sourceTree = ""; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ASCollectionView.h; sourceTree = ""; }; AC3C4A501A1139C100143C57 /* ASCollectionView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionView.mm; sourceTree = ""; }; AC3C4A531A113EEC00143C57 /* ASCollectionViewProtocols.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewProtocols.h; sourceTree = ""; }; @@ -642,27 +646,27 @@ AC7A2C161BDE11DF0093FE1A /* ASTableViewInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTableViewInternal.h; sourceTree = ""; }; ACC945A81BA9E7A0005E1FB8 /* ASViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASViewController.h; sourceTree = ""; }; ACE87A2B1D73696800D7FF06 /* ASSectionContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASSectionContext.h; path = Details/ASSectionContext.h; sourceTree = ""; }; - ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASBackgroundLayoutSpec.h; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h; sourceTree = ""; }; - ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASBackgroundLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm; sourceTree = ""; }; - ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASCenterLayoutSpec.h; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.h; sourceTree = ""; }; - ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASCenterLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm; sourceTree = ""; }; - ACF6ED071B17843500DA7C62 /* ASDimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASDimension.h; path = AsyncDisplayKit/Layout/ASDimension.h; sourceTree = ""; }; - ACF6ED081B17843500DA7C62 /* ASDimension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASDimension.mm; path = AsyncDisplayKit/Layout/ASDimension.mm; sourceTree = ""; }; - ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASInsetLayoutSpec.h; path = AsyncDisplayKit/Layout/ASInsetLayoutSpec.h; sourceTree = ""; }; - ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASInsetLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm; sourceTree = ""; }; - ACF6ED0B1B17843500DA7C62 /* ASLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayout.h; path = AsyncDisplayKit/Layout/ASLayout.h; sourceTree = ""; }; - ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayout.mm; path = AsyncDisplayKit/Layout/ASLayout.mm; sourceTree = ""; }; - ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutSpec.h; path = AsyncDisplayKit/Layout/ASLayoutSpec.h; sourceTree = ""; }; - ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASLayoutSpec.mm; sourceTree = ""; }; - ACF6ED111B17843500DA7C62 /* ASLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutElement.h; path = AsyncDisplayKit/Layout/ASLayoutElement.h; sourceTree = ""; }; - ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASOverlayLayoutSpec.h; path = AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h; sourceTree = ""; }; - ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASOverlayLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm; sourceTree = ""; }; - ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASRatioLayoutSpec.h; path = AsyncDisplayKit/Layout/ASRatioLayoutSpec.h; sourceTree = ""; }; - ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASRatioLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm; sourceTree = ""; }; - ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutSpec.h; path = AsyncDisplayKit/Layout/ASStackLayoutSpec.h; sourceTree = ""; }; - ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASStackLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASStackLayoutSpec.mm; sourceTree = ""; }; - ACF6ED181B17843500DA7C62 /* ASAbsoluteLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASAbsoluteLayoutSpec.h; path = AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h; sourceTree = ""; }; - ACF6ED191B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = ASAbsoluteLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm; sourceTree = ""; }; + ACF6ED011B17843500DA7C62 /* ASBackgroundLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBackgroundLayoutSpec.h; sourceTree = ""; }; + ACF6ED021B17843500DA7C62 /* ASBackgroundLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASBackgroundLayoutSpec.mm; sourceTree = ""; }; + ACF6ED031B17843500DA7C62 /* ASCenterLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCenterLayoutSpec.h; sourceTree = ""; }; + ACF6ED041B17843500DA7C62 /* ASCenterLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCenterLayoutSpec.mm; sourceTree = ""; }; + ACF6ED071B17843500DA7C62 /* ASDimension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDimension.h; sourceTree = ""; }; + ACF6ED081B17843500DA7C62 /* ASDimension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDimension.mm; sourceTree = ""; }; + ACF6ED091B17843500DA7C62 /* ASInsetLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASInsetLayoutSpec.h; sourceTree = ""; }; + ACF6ED0A1B17843500DA7C62 /* ASInsetLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASInsetLayoutSpec.mm; sourceTree = ""; }; + ACF6ED0B1B17843500DA7C62 /* ASLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayout.h; sourceTree = ""; }; + ACF6ED0C1B17843500DA7C62 /* ASLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayout.mm; sourceTree = ""; }; + ACF6ED0D1B17843500DA7C62 /* ASLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutSpec.h; sourceTree = ""; }; + ACF6ED0E1B17843500DA7C62 /* ASLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASLayoutSpec.mm; sourceTree = ""; }; + ACF6ED111B17843500DA7C62 /* ASLayoutElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElement.h; sourceTree = ""; }; + ACF6ED121B17843500DA7C62 /* ASOverlayLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASOverlayLayoutSpec.h; sourceTree = ""; }; + ACF6ED131B17843500DA7C62 /* ASOverlayLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASOverlayLayoutSpec.mm; sourceTree = ""; }; + ACF6ED141B17843500DA7C62 /* ASRatioLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRatioLayoutSpec.h; sourceTree = ""; }; + ACF6ED151B17843500DA7C62 /* ASRatioLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASRatioLayoutSpec.mm; sourceTree = ""; }; + ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASStackLayoutSpec.h; sourceTree = ""; }; + ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackLayoutSpec.mm; sourceTree = ""; }; + ACF6ED181B17843500DA7C62 /* ASAbsoluteLayoutSpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASAbsoluteLayoutSpec.h; sourceTree = ""; }; + ACF6ED191B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASAbsoluteLayoutSpec.mm; sourceTree = ""; }; ACF6ED431B17847A00DA7C62 /* ASInternalHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASInternalHelpers.h; sourceTree = ""; }; ACF6ED441B17847A00DA7C62 /* ASInternalHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASInternalHelpers.m; sourceTree = ""; }; ACF6ED531B178DC700DA7C62 /* ASCenterLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCenterLayoutSpecSnapshotTests.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; @@ -686,17 +690,25 @@ B30BF6511C5964B0004FCD53 /* ASLayoutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutManager.m; path = TextKit/ASLayoutManager.m; sourceTree = ""; }; B35061DA1B010EDF0018CF92 /* AsyncDisplayKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AsyncDisplayKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDC2D162BD55A807C1475DA5 /* Pods-AsyncDisplayKitTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.profile.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.profile.xcconfig"; sourceTree = ""; }; + CC0349FF1E5FAF9700626263 /* ASElementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASElementMap.h; sourceTree = ""; }; + CC034A001E5FAF9700626263 /* ASElementMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASElementMap.m; sourceTree = ""; }; + CC034A0B1E60C3D500626263 /* ASRectTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRectTable.h; sourceTree = ""; }; + CC034A0C1E60C3D500626263 /* ASRectTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASRectTable.m; sourceTree = ""; }; + CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASRectTableTests.m; sourceTree = ""; }; + CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AsyncDisplayKit+IGListKitMethods.h"; sourceTree = ""; }; + CC034A121E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AsyncDisplayKit+IGListKitMethods.m"; sourceTree = ""; }; CC051F1E1D7A286A006434CB /* ASCALayerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCALayerTests.m; sourceTree = ""; }; CC0AEEA31D66316E005D1C78 /* ASUICollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASUICollectionViewTests.m; sourceTree = ""; }; CC0F88591E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewFlowLayoutInspector.m; sourceTree = ""; }; CC0F885A1E42807F00576FED /* ASCollectionViewFlowLayoutInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionViewFlowLayoutInspector.h; sourceTree = ""; }; CC0F885D1E4280B800576FED /* _ASCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASCollectionViewCell.m; sourceTree = ""; }; CC0F885E1E4280B800576FED /* _ASCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASCollectionViewCell.h; sourceTree = ""; }; - CC0F88681E4286FA00576FED /* ReferenceImages_32 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages_32; sourceTree = ""; }; CC0F88691E4286FA00576FED /* ReferenceImages_64 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages_64; sourceTree = ""; }; CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ReferenceImages_iOS_10; sourceTree = ""; }; CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASNetworkImageNodeTests.m; sourceTree = ""; }; CC2E317F1DAC353700EEE891 /* ASCollectionView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCollectionView+Undeprecated.h"; sourceTree = ""; }; + CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMutableElementMap.h; sourceTree = ""; }; + CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASMutableElementMap.m; sourceTree = ""; }; CC3B20811C3F76D600798563 /* ASPendingStateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPendingStateController.h; sourceTree = ""; }; CC3B20821C3F76D600798563 /* ASPendingStateController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASPendingStateController.mm; sourceTree = ""; }; CC3B20871C3F7A5400798563 /* ASWeakSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakSet.h; sourceTree = ""; }; @@ -711,12 +723,16 @@ CC512B841DAC45C60054848E /* ASTableView+Undeprecated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASTableView+Undeprecated.h"; sourceTree = ""; }; CC54A81B1D70077A00296A24 /* ASDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASDispatch.h; sourceTree = ""; }; CC54A81D1D7008B300296A24 /* ASDispatchTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ASDispatchTests.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIResponder+AsyncDisplayKit.h"; sourceTree = ""; }; + CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIResponder+AsyncDisplayKit.m"; sourceTree = ""; }; + CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASResponderChainEnumerator.h; sourceTree = ""; }; + CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASResponderChainEnumerator.m; sourceTree = ""; }; CC57EAF91E394EA40034C595 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CC58AA4A1E398E1D002C8CB4 /* ASBlockTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASBlockTypes.h; sourceTree = ""; }; CC7FD9DC1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPhotosFrameworkImageRequest.h; sourceTree = ""; }; CC7FD9DD1BB5E962005CCB2B /* ASPhotosFrameworkImageRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequest.m; sourceTree = ""; }; CC7FD9E01BB5F750005CCB2B /* ASPhotosFrameworkImageRequestTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPhotosFrameworkImageRequestTests.m; sourceTree = ""; }; - CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ASCellNode+Internal.h"; path = "AsyncDisplayKit/ASCellNode+Internal.h"; sourceTree = SOURCE_ROOT; }; + CC87BB941DA8193C0090E380 /* ASCellNode+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASCellNode+Internal.h"; sourceTree = ""; }; CC8B05D41D73836400F54286 /* ASPerformanceTestContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPerformanceTestContext.h; sourceTree = ""; }; CC8B05D51D73836400F54286 /* ASPerformanceTestContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASPerformanceTestContext.m; sourceTree = ""; }; CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTextNodePerformanceTests.m; sourceTree = ""; }; @@ -728,7 +744,6 @@ CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "IGListAdapter+AsyncDisplayKit.h"; sourceTree = ""; }; CCE04B211E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "IGListAdapter+AsyncDisplayKit.m"; sourceTree = ""; }; CCE04B2B1E314A32006AEBBB /* ASSupplementaryNodeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASSupplementaryNodeSource.h; sourceTree = ""; }; - CCF92DCC1E3155180019E9C6 /* ASIGListKitMethodImplementations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIGListKitMethodImplementations.h; sourceTree = ""; }; D3779BCFF841AD3EB56537ED /* Pods-AsyncDisplayKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.release.xcconfig"; sourceTree = ""; }; D785F6601A74327E00291744 /* ASScrollNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASScrollNode.h; sourceTree = ""; }; D785F6611A74327E00291744 /* ASScrollNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASScrollNode.mm; sourceTree = ""; }; @@ -751,9 +766,9 @@ DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; 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 = ""; }; - E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutElement.mm; path = AsyncDisplayKit/Layout/ASLayoutElement.mm; sourceTree = ""; }; - E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASIndexedNodeContext.h; sourceTree = ""; }; - E5711A2D1C840C96009619D4 /* ASIndexedNodeContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASIndexedNodeContext.mm; sourceTree = ""; }; + E55D86311CA8A14000A0C26F /* ASLayoutElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASLayoutElement.mm; sourceTree = ""; }; + E5711A2A1C840C81009619D4 /* ASCollectionElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionElement.h; sourceTree = ""; }; + E5711A2D1C840C96009619D4 /* ASCollectionElement.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASCollectionElement.mm; sourceTree = ""; }; EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; F711994D1D20C21100568860 /* ASDisplayNodeExtrasTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeExtrasTests.m; sourceTree = ""; }; FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.debug.xcconfig"; sourceTree = ""; }; @@ -795,13 +810,12 @@ 057D02C01AC0A66700C7AC3C /* AsyncDisplayKitTestHost */ = { isa = PBXGroup; children = ( - 204C979D1B362CB3002B1083 /* Default-568h@2x.png */, 057D02C51AC0A66700C7AC3C /* AppDelegate.h */, - 057D02C61AC0A66700C7AC3C /* AppDelegate.mm */, + 057D02C61AC0A66700C7AC3C /* AppDelegate.m */, 057D02C11AC0A66700C7AC3C /* Supporting Files */, ); name = AsyncDisplayKitTestHost; - path = ../AsyncDisplayKitTestHost; + path = TestHost; sourceTree = ""; }; 057D02C11AC0A66700C7AC3C /* Supporting Files */ = { @@ -816,7 +830,7 @@ 058D09A3195D04C000B7D73C = { isa = PBXGroup; children = ( - 058D09B1195D04C000B7D73C /* AsyncDisplayKit */, + 058D09B1195D04C000B7D73C /* Source */, 058D09C5195D04C000B7D73C /* Tests */, 058D09AE195D04C000B7D73C /* Frameworks */, 058D09AD195D04C000B7D73C /* Products */, @@ -851,7 +865,7 @@ name = Frameworks; sourceTree = ""; }; - 058D09B1195D04C000B7D73C /* AsyncDisplayKit */ = { + 058D09B1195D04C000B7D73C /* Source */ = { isa = PBXGroup; children = ( CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */, @@ -927,23 +941,25 @@ DB55C2651C641AE4004EDCF5 /* ASContextTransitioning.h */, 68FC85E71CE29C7D00EDD713 /* ASVisibilityProtocols.h */, 68FC85E81CE29C7D00EDD713 /* ASVisibilityProtocols.m */, - 690ED5911E36D118000627C0 /* tvOS */, + CC55A70B1E529FA200594372 /* UIResponder+AsyncDisplayKit.h */, + CC55A70C1E529FA200594372 /* UIResponder+AsyncDisplayKit.m */, + 058D0A42195D058D00B7D73C /* Base */, DE89C1691DCEB9CC00D49D74 /* Debug */, 058D09E1195D050800B7D73C /* Details */, 058D0A01195D050800B7D73C /* Private */, AC6456051B0A333200CF11B8 /* Layout */, 257754661BED245B00737CA5 /* TextKit */, + 690ED5911E36D118000627C0 /* tvOS */, 058D09B2195D04C000B7D73C /* Supporting Files */, ); - path = AsyncDisplayKit; + path = Source; sourceTree = ""; }; 058D09B2195D04C000B7D73C /* Supporting Files */ = { isa = PBXGroup; children = ( CC57EAF91E394EA40034C595 /* Info.plist */, - 058D0A42195D058D00B7D73C /* Base */, - 044285011BAA3CC700D16268 /* AsyncDisplayKit.modulemap */, + 044285011BAA3CC700D16268 /* module.modulemap */, ); name = "Supporting Files"; sourceTree = ""; @@ -951,6 +967,7 @@ 058D09C5195D04C000B7D73C /* Tests */ = { isa = PBXGroup; children = ( + CC034A0F1E60C9BF00626263 /* ASRectTableTests.m */, CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.m */, CC051F1E1D7A286A006434CB /* ASCALayerTests.m */, CC8B05D71D73979700F54286 /* ASTextNodePerformanceTests.m */, @@ -991,7 +1008,7 @@ ACF6ED541B178DC700DA7C62 /* ASDimensionTests.mm */, 058D0A2D195D057000B7D73C /* ASDisplayLayerTests.m */, 058D0A2E195D057000B7D73C /* ASDisplayNodeAppearanceTests.m */, - 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.m */, + 058D0A2F195D057000B7D73C /* ASDisplayNodeTests.mm */, 69B225661D72535E00B25B22 /* ASDisplayNodeLayoutTests.mm */, 058D0A30195D057000B7D73C /* ASDisplayNodeTestsHelper.h */, 058D0A31195D057000B7D73C /* ASDisplayNodeTestsHelper.m */, @@ -1014,14 +1031,12 @@ 695BE2541DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm */, 699B83501E3C1BA500433FA4 /* ASLayoutSpecTests.m */, ); - name = Tests; - path = AsyncDisplayKitTests; + path = Tests; sourceTree = ""; }; 058D09C6195D04C000B7D73C /* Supporting Files */ = { isa = PBXGroup; children = ( - CC0F88681E4286FA00576FED /* ReferenceImages_32 */, CC0F88691E4286FA00576FED /* ReferenceImages_64 */, CC0F886A1E4286FA00576FED /* ReferenceImages_iOS_10 */, 058D09C7195D04C000B7D73C /* AsyncDisplayKitTests-Info.plist */, @@ -1051,6 +1066,8 @@ 68C215571DE10D330019C4BC /* ASCollectionViewLayoutInspector.m */, 205F0E1B1B373A2C007741D0 /* ASCollectionViewLayoutController.h */, 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */, + 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */, + 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */, 4640521B1A3F83C40061C0BA /* ASTableLayoutController.h */, 4640521C1A3F83C40061C0BA /* ASTableLayoutController.m */, 058D09E6195D050800B7D73C /* ASHighlightOverlayLayer.h */, @@ -1120,6 +1137,14 @@ 058D0A01195D050800B7D73C /* Private */ = { isa = PBXGroup; children = ( + CC2F65EC1E5FFB1600DA57C9 /* ASMutableElementMap.h */, + CC2F65ED1E5FFB1600DA57C9 /* ASMutableElementMap.m */, + CC0349FF1E5FAF9700626263 /* ASElementMap.h */, + CC034A001E5FAF9700626263 /* ASElementMap.m */, + CC034A0B1E60C3D500626263 /* ASRectTable.h */, + CC034A0C1E60C3D500626263 /* ASRectTable.m */, + CC55A70F1E52A0F200594372 /* ASResponderChainEnumerator.h */, + CC55A7101E52A0F200594372 /* ASResponderChainEnumerator.m */, 6947B0BB1E36B4E30007C478 /* Layout */, CCE04B2A1E313EDA006AEBBB /* Collection Data Adapter */, 058D0A03195D050800B7D73C /* _ASCoreAnimationExtras.h */, @@ -1140,7 +1165,6 @@ CC0F88591E42807F00576FED /* ASCollectionViewFlowLayoutInspector.m */, 9F98C0231DBDF2A300476D92 /* ASControlTargetAction.h */, 9F98C0241DBDF2A300476D92 /* ASControlTargetAction.m */, - 251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */, 8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */, 8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */, AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */, @@ -1155,8 +1179,6 @@ 058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */, 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */, 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */, - 696F01EA1DD2AF450049FBD5 /* ASEventLog.h */, - 696F01EB1DD2AF450049FBD5 /* ASEventLog.mm */, 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */, 058D0A0D195D050800B7D73C /* ASImageNode+CGExtras.h */, 058D0A0E195D050800B7D73C /* ASImageNode+CGExtras.m */, @@ -1185,7 +1207,7 @@ 0516FA3B1A15563400B4EBED /* ASLog.h */, ); path = Base; - sourceTree = SOURCE_ROOT; + sourceTree = ""; }; 257754661BED245B00737CA5 /* TextKit */ = { isa = PBXGroup; @@ -1229,12 +1251,10 @@ children = ( DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */, DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */, - 251B8EF21BBB3D690087C538 /* ASCollectionDataController.h */, - 251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */, 464052191A3F83C40061C0BA /* ASDataController.h */, 4640521A1A3F83C40061C0BA /* ASDataController.mm */, - E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */, - E5711A2D1C840C96009619D4 /* ASIndexedNodeContext.mm */, + E5711A2A1C840C81009619D4 /* ASCollectionElement.h */, + E5711A2D1C840C96009619D4 /* ASCollectionElement.mm */, AC6145401D8AFAE8003D62A2 /* ASSection.h */, AC6145421D8AFD4F003D62A2 /* ASSection.m */, ); @@ -1308,8 +1328,7 @@ ACF6ED161B17843500DA7C62 /* ASStackLayoutSpec.h */, ACF6ED171B17843500DA7C62 /* ASStackLayoutSpec.mm */, ); - name = Layout; - path = ..; + path = Layout; sourceTree = ""; }; CCE04B1D1E313E99006AEBBB /* Collection Data Adapter */ = { @@ -1334,7 +1353,8 @@ CCF92DCE1E315FC50019E9C6 /* IGListKit Support */ = { isa = PBXGroup; children = ( - CCF92DCC1E3155180019E9C6 /* ASIGListKitMethodImplementations.h */, + CC034A111E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h */, + CC034A121E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m */, CCE04B201E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.h */, CCE04B211E313EB9006AEBBB /* IGListAdapter+AsyncDisplayKit.m */, ); @@ -1396,8 +1416,10 @@ B35062591B010F070018CF92 /* ASBaseDefines.h in Headers */, B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, + CC034A0D1E60C3D500626263 /* ASRectTable.h in Headers */, B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, + CC55A7111E52A0F200594372 /* ASResponderChainEnumerator.h in Headers */, 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, B35061F51B010EFD0018CF92 /* ASCollectionView.h in Headers */, ACE87A2C1D73696800D7FF06 /* ASSectionContext.h in Headers */, @@ -1441,6 +1463,7 @@ 254C6B771BF94DF4003EC431 /* ASTextKitAttributes.h in Headers */, 254C6B7D1BF94DF4003EC431 /* ASTextKitShadower.h in Headers */, 690ED58E1E36BCA6000627C0 /* ASLayoutElementStylePrivate.h in Headers */, + CC55A70D1E529FA200594372 /* UIResponder+AsyncDisplayKit.h in Headers */, 254C6B731BF94DF4003EC431 /* ASTextKitCoreTextAdditions.h in Headers */, 254C6B7A1BF94DF4003EC431 /* ASTextKitRenderer.h in Headers */, 69CB62AC1CB8165900024920 /* _ASDisplayViewAccessiblity.h in Headers */, @@ -1450,7 +1473,6 @@ 044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */, AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */, CC87BB951DA8193C0090E380 /* ASCellNode+Internal.h in Headers */, - 9C70F20C1CDBE9B6007D6C76 /* ASCollectionDataController.h in Headers */, 9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */, 254C6B791BF94DF4003EC431 /* ASTextKitEntityAttribute.h in Headers */, CC3B20841C3F76D600798563 /* ASPendingStateController.h in Headers */, @@ -1462,16 +1484,17 @@ CC57EAF71E3939350034C595 /* ASCollectionView+Undeprecated.h in Headers */, B35062521B010EFD0018CF92 /* ASDisplayNodeInternal.h in Headers */, AC7A2C181BDE11DF0093FE1A /* ASTableViewInternal.h in Headers */, + CC034A011E5FAF9700626263 /* ASElementMap.h in Headers */, B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */, 254C6B7F1BF94DF4003EC431 /* ASTextKitTruncating.h in Headers */, CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */, 6977965F1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h in Headers */, 692BE8D71E36B65B00C86D87 /* ASLayoutSpecPrivate.h in Headers */, - 9C70F20B1CDBE9A4007D6C76 /* ASDataController+Subclasses.h in Headers */, 34EFC75D1B701BE900AD841F /* ASInternalHelpers.h in Headers */, DEC146B71C37A16A004A0EE7 /* ASCollectionInternal.h in Headers */, 68B8A4E21CBDB958007E4543 /* ASWeakProxy.h in Headers */, 9F98C0271DBE29FC00476D92 /* ASControlTargetAction.h in Headers */, + CC034A131E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.h in Headers */, 690ED5961E36D118000627C0 /* ASControlNode+tvOS.h in Headers */, 695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */, 0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, @@ -1485,7 +1508,7 @@ CCF18FF41D2575E300DF5895 /* NSIndexSet+ASHelpers.h in Headers */, 83A7D95C1D44548100BF333E /* ASWeakMap.h in Headers */, 69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */, - E5711A2C1C840C81009619D4 /* ASIndexedNodeContext.h in Headers */, + E5711A2C1C840C81009619D4 /* ASCollectionElement.h in Headers */, 6947B0BE1E36B4E30007C478 /* ASStackUnpositionedLayout.h in Headers */, CC4C2A771D88E3BF0039ACAB /* ASTraceEvent.h in Headers */, 254C6B7B1BF94DF4003EC431 /* ASTextKitRenderer+Positioning.h in Headers */, @@ -1495,6 +1518,7 @@ 9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */, B35062201B010EFD0018CF92 /* ASLayoutController.h in Headers */, B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */, + CC2F65EE1E5FFB1600DA57C9 /* ASMutableElementMap.h in Headers */, 34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */, B350625C1B010F070018CF92 /* ASLog.h in Headers */, CC3B208A1C3F7A5400798563 /* ASWeakSet.h in Headers */, @@ -1645,7 +1669,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 204C979E1B362CB3002B1083 /* Default-568h@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1653,7 +1676,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - CC0F886B1E4286FA00576FED /* ReferenceImages_32 in Resources */, CC0F886C1E4286FA00576FED /* ReferenceImages_64 in Resources */, CC0F886D1E4286FA00576FED /* ReferenceImages_iOS_10 in Resources */, 052EE06B1A15A0D8002C6279 /* TestResources in Resources */, @@ -1723,7 +1745,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 057D02C71AC0A66700C7AC3C /* AppDelegate.mm in Sources */, + 057D02C71AC0A66700C7AC3C /* AppDelegate.m in Sources */, 057D02C41AC0A66700C7AC3C /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1740,6 +1762,7 @@ 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.mm in Sources */, 2911485C1A77147A005D0878 /* ASControlNodeTests.m in Sources */, CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.m in Sources */, + CC034A101E60C9BF00626263 /* ASRectTableTests.m in Sources */, F711994E1D20C21100568860 /* ASDisplayNodeExtrasTests.m in Sources */, ACF6ED5D1B178DC700DA7C62 /* ASDimensionTests.mm in Sources */, 695BE2551DC1245C008E6EA5 /* ASWrapperSpecSnapshotTests.mm in Sources */, @@ -1749,8 +1772,7 @@ 058D0A39195D057000B7D73C /* ASDisplayNodeAppearanceTests.m in Sources */, CCB2F34D1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m in Sources */, AE6987C11DD04E1000B9E458 /* ASPagerNodeTests.m in Sources */, - 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.m in Sources */, - 699B83511E3C1BA500433FA4 /* ASLayoutSpecTests.m in Sources */, + 058D0A3A195D057000B7D73C /* ASDisplayNodeTests.mm in Sources */, 696FCB311D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm in Sources */, 69FEE53D1D95A9AF0086F066 /* ASLayoutElementStyleTests.m in Sources */, CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.m in Sources */, @@ -1801,7 +1823,6 @@ 690C35671E0567C600069B91 /* ASDimensionDeprecated.mm in Sources */, 92DD2FE71BF4D0850074C9DD /* ASMapNode.mm in Sources */, 636EA1A51C7FF4EF00EE152F /* ASDefaultPlayButton.m in Sources */, - 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.mm in Sources */, 6947B0C51E36B5040007C478 /* ASStackPositionedLayout.mm in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, @@ -1830,9 +1851,11 @@ 18C2ED831B9B7DE800F627B3 /* ASCollectionNode.mm in Sources */, E55D86331CA8A14000A0C26F /* ASLayoutElement.mm in Sources */, 68FC85EC1CE29C7D00EDD713 /* ASVisibilityProtocols.m in Sources */, + CC55A7121E52A0F200594372 /* ASResponderChainEnumerator.m in Sources */, 68B8A4E41CBDB958007E4543 /* ASWeakProxy.m in Sources */, 9C70F20A1CDBE949007D6C76 /* ASTableNode.mm in Sources */, 69CB62AE1CB8165900024920 /* _ASDisplayViewAccessiblity.mm in Sources */, + CC034A021E5FAF9700626263 /* ASElementMap.m in Sources */, B35061F61B010EFD0018CF92 /* ASCollectionView.mm in Sources */, 509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */, B35061F91B010EFD0018CF92 /* ASControlNode.mm in Sources */, @@ -1846,8 +1869,9 @@ B35062501B010EFD0018CF92 /* ASDisplayNode+DebugTiming.mm in Sources */, DEC146B91C37A16A004A0EE7 /* ASCollectionInternal.m in Sources */, 254C6B891BF94F8A003EC431 /* ASTextKitRenderer+Positioning.mm in Sources */, + CC034A0E1E60C3D500626263 /* ASRectTable.m in Sources */, 68355B341CB579B9001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */, - E5711A301C840C96009619D4 /* ASIndexedNodeContext.mm in Sources */, + E5711A301C840C96009619D4 /* ASCollectionElement.mm in Sources */, B35062511B010EFD0018CF92 /* ASDisplayNode+UIViewBridge.mm in Sources */, B35061FC1B010EFD0018CF92 /* ASDisplayNode.mm in Sources */, B35061FF1B010EFD0018CF92 /* ASDisplayNodeExtras.mm in Sources */, @@ -1858,6 +1882,7 @@ B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */, 9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.m in Sources */, CC0F885F1E4280B800576FED /* _ASCollectionViewCell.m in Sources */, + CC2F65EF1E5FFB1600DA57C9 /* ASMutableElementMap.m in Sources */, B35062541B010EFD0018CF92 /* ASImageNode+CGExtras.m in Sources */, 6947B0C01E36B4E30007C478 /* ASStackUnpositionedLayout.mm in Sources */, 68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */, @@ -1904,10 +1929,12 @@ B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */, 6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */, 68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */, + CC034A141E649F1300626263 /* AsyncDisplayKit+IGListKitMethods.m in Sources */, 509E68661B3AEDD7009B9150 /* CoreGraphics+ASConvenience.m in Sources */, 254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */, 34566CB31BC1213700715E6B /* ASPhotosFrameworkImageRequest.m in Sources */, 254C6B831BF94F8A003EC431 /* ASTextKitCoreTextAdditions.m in Sources */, + CC55A70E1E529FA200594372 /* UIResponder+AsyncDisplayKit.m in Sources */, 697796611D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.mm in Sources */, B350623B1B010EFD0018CF92 /* NSMutableAttributedString+TextKitAdditions.m in Sources */, 044284FD1BAA365100D16268 /* UICollectionViewLayout+ASConvenience.m in Sources */, @@ -1951,7 +1978,7 @@ "DEBUG=1", "$(inherited)", ); - INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist; + INFOPLIST_FILE = Tests/TestHost/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; @@ -1968,7 +1995,7 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; - INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist; + INFOPLIST_FILE = Tests/TestHost/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; @@ -2078,11 +2105,10 @@ "DEBUG=1", "$(inherited)", "COCOAPODS=1", - "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"", ); GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - INFOPLIST_FILE = "AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist"; + INFOPLIST_FILE = "Tests/AsyncDisplayKitTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDisplayKitTestHost.app/AsyncDisplayKitTestHost"; @@ -2104,11 +2130,10 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "COCOAPODS=1", - "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"", ); GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - INFOPLIST_FILE = "AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist"; + INFOPLIST_FILE = "Tests/AsyncDisplayKitTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDisplayKitTestHost.app/AsyncDisplayKitTestHost"; @@ -2132,12 +2157,12 @@ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; + GCC_PREFIX_HEADER = "Source/AsyncDisplayKit-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit/Info.plist"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = AsyncDisplayKit/AsyncDisplayKit.modulemap; + MODULEMAP_FILE = Source/module.modulemap; MTL_ENABLE_DEBUG_INFO = YES; OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; @@ -2166,11 +2191,11 @@ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; - INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit/Info.plist"; + GCC_PREFIX_HEADER = "Source/AsyncDisplayKit-Prefix.pch"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = AsyncDisplayKit/AsyncDisplayKit.modulemap; + MODULEMAP_FILE = Source/module.modulemap; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; @@ -2236,11 +2261,10 @@ GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "COCOAPODS=1", - "FB_REFERENCE_IMAGE_DIR=\"\\\"$(SOURCE_ROOT)/$(PROJECT_NAME)Tests/ReferenceImages\\\"\"", ); GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; - INFOPLIST_FILE = "AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist"; + INFOPLIST_FILE = "Tests/AsyncDisplayKitTests-Info.plist"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AsyncDisplayKitTestHost.app/AsyncDisplayKitTestHost"; @@ -2257,7 +2281,7 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; - INFOPLIST_FILE = AsyncDisplayKitTestHost/Info.plist; + INFOPLIST_FILE = Tests/TestHost/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; @@ -2282,11 +2306,11 @@ GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "AsyncDisplayKit/AsyncDisplayKit-Prefix.pch"; - INFOPLIST_FILE = "$(SRCROOT)/AsyncDisplayKit/Info.plist"; + GCC_PREFIX_HEADER = "Source/AsyncDisplayKit-Prefix.pch"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MODULEMAP_FILE = AsyncDisplayKit/AsyncDisplayKit.modulemap; + MODULEMAP_FILE = Source/module.modulemap; MTL_ENABLE_DEBUG_INFO = NO; OTHER_CFLAGS = "-DMINIMAL_ASDK=1"; PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)"; diff --git a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme index 2c956151d7..6d03ba5d34 100644 --- a/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme +++ b/AsyncDisplayKit.xcodeproj/xcshareddata/xcschemes/AsyncDisplayKit.xcscheme @@ -75,6 +75,13 @@ ReferencedContainer = "container:AsyncDisplayKit.xcodeproj"> + + + + diff --git a/AsyncDisplayKit/ASIGListKitMethodImplementations.h b/AsyncDisplayKit/ASIGListKitMethodImplementations.h deleted file mode 100644 index 0caac0434a..0000000000 --- a/AsyncDisplayKit/ASIGListKitMethodImplementations.h +++ /dev/null @@ -1,66 +0,0 @@ -// -// ASIGListKitMethodImplementations.h -// AsyncDisplayKit -// -// Created by Adlai Holler on 1/19/17. -// Copyright © 2017 Facebook. All rights reserved. -// - -/** - * If you are using AsyncDisplayKit with IGListKit, you should use - * these macros to provide implementations of methods like - * -cellForItemAtIndex: that don't apply when used with AsyncDisplayKit. - * - * Your section controllers should also conform to @c ASSectionController and your - * supplementary view sources should conform to @c ASSupplementaryNodeSource. - */ - -#if IG_LIST_KIT - -#import - -/** - * The implementation of viewForSupplementaryElementOfKind that connects - * IGSupplementaryViewSource to AsyncDisplayKit. Add this into the .m file - * for your `ASIGListSupplementaryViewSource` and implement the ASDK-specific - * method `nodeForSupplementaryElementOfKind:` to provide your node. - * - * @param sectionController The section controller this supplementary source is - * working on behalf of. For example, `self` or `self.sectionController`. - */ -#define ASIGSupplementarySourceViewForSupplementaryElementImplementation(sectionController) \ -- (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index { \ - return [self.collectionContext dequeueReusableSupplementaryViewOfKind:elementKind forSectionController:sectionController class:[UICollectionReusableView class] atIndex:index]; \ -} - -/** - * The implementation of sizeForSupplementaryViewOfKind that connects - * IGSupplementaryViewSource to AsyncDisplayKit. Add this into the .m file - * for your `ASIGListSupplementaryViewSource` and implement the ASDK-specific - * method `nodeForSupplementaryElementOfKind:` to provide your node which should - * size itself. You can set `node.style.preferredSize` if you want to fix the size. - * - * @param sectionController The section controller this supplementary source is - * working on behalf of. For example, `self` or `self.sectionController`. - */ -#define ASIGSupplementarySourceSizeForSupplementaryElementImplementation \ -- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index {\ - ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); \ - return CGSizeZero; \ -} - - -#define ASIGSectionControllerCellForIndexImplementation \ -- (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index\ -{\ - return [self.collectionContext dequeueReusableCellOfClass:[_ASCollectionViewCell class] forSectionController:self atIndex:index]; \ -}\ - -#define ASIGSectionControllerSizeForItemImplementation \ -- (CGSize)sizeForItemAtIndex:(NSInteger)index \ -{\ - ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); \ - return CGSizeZero;\ -} - -#endif // IG_LIST_KIT diff --git a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm b/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm deleted file mode 100644 index f27cc15843..0000000000 --- a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.mm +++ /dev/null @@ -1,111 +0,0 @@ -// -// ASStackPositionedLayout.mm -// AsyncDisplayKit -// -// 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. -// - -#import - -#import - -#import -#import -#import - -static CGFloat crossOffset(const ASStackLayoutSpecStyle &style, - const ASStackLayoutSpecItem &l, - const CGFloat crossSize, - const CGFloat baseline) -{ - switch (alignment(l.child.style.alignSelf, style.alignItems)) { - case ASStackLayoutAlignItemsEnd: - return crossSize - crossDimension(style.direction, l.layout.size); - case ASStackLayoutAlignItemsCenter: - return ASFloorPixelValue((crossSize - crossDimension(style.direction, l.layout.size)) / 2); - case ASStackLayoutAlignItemsBaselineFirst: - case ASStackLayoutAlignItemsBaselineLast: - return baseline - ASStackUnpositionedLayout::baselineForItem(style, l); - case ASStackLayoutAlignItemsStart: - case ASStackLayoutAlignItemsStretch: - return 0; - } -} - -/** - * Positions children according to the stack style and positioning properties. - * - * @param style The layout style of the overall stack layout - * @param firstChildOffset Offset of the first child - * @param extraSpacing Spacing between children, in addition to spacing set to the stack's layout style - * @param unpositionedLayout Unpositioned children of the stack - * @param constrainedSize Constrained size of the stack - */ -static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style, - const CGFloat firstChildOffset, - const CGFloat extraSpacing, - const ASStackUnpositionedLayout &unpositionedLayout, - const ASSizeRange &constrainedSize) -{ - CGFloat crossSize = unpositionedLayout.crossSize; - CGFloat baseline = unpositionedLayout.baseline; - - // Adjust the position of the unpositioned layouts to be positioned - CGPoint p = directionPoint(style.direction, firstChildOffset, 0); - BOOL first = YES; - const auto stackedChildren = unpositionedLayout.items; - for (const auto &l : stackedChildren) { - p = p + directionPoint(style.direction, l.child.style.spacingBefore, 0); - if (!first) { - p = p + directionPoint(style.direction, style.spacing + extraSpacing, 0); - } - first = NO; - l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize, baseline)); - - p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.style.spacingAfter, 0); - } - - return {std::move(stackedChildren)}; -} - -ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &unpositionedLayout, - const ASStackLayoutSpecStyle &style, - const ASSizeRange &constrainedSize) -{ - const auto numOfItems = unpositionedLayout.items.size(); - ASDisplayNodeCAssertTrue(numOfItems > 0); - const CGFloat violation = unpositionedLayout.violation; - ASStackLayoutJustifyContent justifyContent = style.justifyContent; - - // Handle edge cases of "space between" and "space around" - if (justifyContent == ASStackLayoutJustifyContentSpaceBetween && (violation < 0 || numOfItems == 1)) { - justifyContent = ASStackLayoutJustifyContentStart; - } else if (justifyContent == ASStackLayoutJustifyContentSpaceAround && (violation < 0 || numOfItems == 1)) { - justifyContent = ASStackLayoutJustifyContentCenter; - } - - switch (justifyContent) { - case ASStackLayoutJustifyContentStart: { - return stackedLayout(style, 0, 0, unpositionedLayout, constrainedSize); - } - case ASStackLayoutJustifyContentCenter: { - return stackedLayout(style, std::floor(violation / 2), 0, unpositionedLayout, constrainedSize); - } - case ASStackLayoutJustifyContentEnd: { - return stackedLayout(style, violation, 0, unpositionedLayout, constrainedSize); - } - case ASStackLayoutJustifyContentSpaceBetween: { - // Spacing between the items, no spaces at the edges, evenly distributed - const auto numOfSpacings = numOfItems - 1; - return stackedLayout(style, 0, violation / numOfSpacings, unpositionedLayout, constrainedSize); - } - case ASStackLayoutJustifyContentSpaceAround: { - // Spacing between items are twice the spacing on the edges - CGFloat spacingUnit = violation / (numOfItems * 2); - return stackedLayout(style, spacingUnit, spacingUnit * 2, unpositionedLayout, constrainedSize); - } - } -} diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png deleted file mode 100644 index 69e7392384..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png deleted file mode 100644 index 28036afad5..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png deleted file mode 100644 index 692c2a4e84..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png deleted file mode 100644 index 7011b35abf..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png deleted file mode 100644 index b14c267b42..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png deleted file mode 100644 index 270b15feb6..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png b/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png deleted file mode 100644 index 7fddbff94e..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@2x.png deleted file mode 100644 index 50cb613c24..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@2x.png deleted file mode 100644 index 50cb613c24..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png deleted file mode 100644 index 02717f8fdb..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png deleted file mode 100644 index 311ef9ed32..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png deleted file mode 100644 index 385fc3e817..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png deleted file mode 100644 index 04ee2d6115..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png and /dev/null differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png b/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png deleted file mode 100644 index 3d9d16b5d2..0000000000 Binary files a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png and /dev/null differ diff --git a/BUCK b/BUCK index c979ec49c1..99f99cad8e 100755 --- a/BUCK +++ b/BUCK @@ -15,21 +15,21 @@ COMMON_LANG_PREPROCESSOR_FLAGS = { COMMON_LINKER_FLAGS = ['-ObjC++'] ASYNCDISPLAYKIT_EXPORTED_HEADERS = glob([ - 'AsyncDisplayKit/*.h', - 'AsyncDisplayKit/Details/**/*.h', - 'AsyncDisplayKit/Layout/*.h', - 'Base/*.h', - 'AsyncDisplayKit/Debug/ASLayoutElementInspectorNode.h', + 'Source/*.h', + 'Source/Details/**/*.h', + 'Source/Layout/*.h', + 'Source/Base/*.h', + 'Source/Debug/AsyncDisplayKit+Debug.h', # Most TextKit components are not public because the C++ content # in the headers will cause build errors when using # `use_frameworks!` on 0.39.0 & Swift 2.1. # See https://github.com/facebook/AsyncDisplayKit/issues/1153 - 'AsyncDisplayKit/TextKit/ASTextNodeTypes.h', - 'AsyncDisplayKit/TextKit/ASTextKitComponents.h' + 'Source/TextKit/ASTextNodeTypes.h', + 'Source/TextKit/ASTextKitComponents.h' ]) ASYNCDISPLAYKIT_PRIVATE_HEADERS = glob([ - 'AsyncDisplayKit/**/*.h' + 'Source/**/*.h' ], excludes = ASYNCDISPLAYKIT_EXPORTED_HEADERS, ) @@ -42,14 +42,14 @@ def asyncdisplaykit_library( apple_library( name = name, - prefix_header = 'AsyncDisplayKit/AsyncDisplayKit-Prefix.pch', + prefix_header = 'Source/AsyncDisplayKit-Prefix.pch', header_path_prefix = 'AsyncDisplayKit', exported_headers = ASYNCDISPLAYKIT_EXPORTED_HEADERS, headers = ASYNCDISPLAYKIT_PRIVATE_HEADERS, srcs = glob([ - 'AsyncDisplayKit/**/*.m', - 'AsyncDisplayKit/**/*.mm', - 'Base/*.m' + 'Source/**/*.m', + 'Source/**/*.mm', + 'Source/Base/*.m' ]), preprocessor_flags = COMMON_PREPROCESSOR_FLAGS + additional_preprocessor_flags, lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS, @@ -63,7 +63,6 @@ def asyncdisplaykit_library( frameworks = [ '$SDKROOT/System/Library/Frameworks/Foundation.framework', '$SDKROOT/System/Library/Frameworks/UIKit.framework', - '$SDKROOT/System/Library/Frameworks/AssetsLibrary.framework', '$SDKROOT/System/Library/Frameworks/QuartzCore.framework', '$SDKROOT/System/Library/Frameworks/CoreMedia.framework', @@ -71,6 +70,9 @@ def asyncdisplaykit_library( '$SDKROOT/System/Library/Frameworks/CoreGraphics.framework', '$SDKROOT/System/Library/Frameworks/CoreLocation.framework', '$SDKROOT/System/Library/Frameworks/AVFoundation.framework', + + # TODO somehow AssetsLibrary can't be weak_framework + '$SDKROOT/System/Library/Frameworks/AssetsLibrary.framework', ] + additional_frameworks, visibility = ['PUBLIC'], ) @@ -99,17 +101,11 @@ for name in ['AsyncDisplayKit', 'AsyncDisplayKit-PINRemoteImage']: # Test Host # TODO: Split to smaller BUCK files and parse in parallel ##################################### -apple_resource( - name = 'TestHostResources', - files = ['Default-568h@2x.png'], - dirs = [], -) - apple_bundle( name = 'TestHost', binary = ':TestHostBinary', extension = 'app', - info_plist = 'AsyncDisplayKitTestHost/Info.plist', + info_plist = 'Tests/TestHost/Info.plist', info_plist_substitutions = { 'PRODUCT_BUNDLE_IDENTIFIER': 'com.facebook.AsyncDisplayKitTestHost', }, @@ -118,15 +114,11 @@ apple_bundle( apple_binary( name = 'TestHostBinary', - headers = glob(['AsyncDisplayKitTestHost/*.h']), - srcs = glob([ - 'AsyncDisplayKitTestHost/*.m', - 'AsyncDisplayKitTestHost/*.mm', - ]), + headers = glob(['Tests/TestHost/*.h']), + srcs = glob(['Tests/TestHost/*.m']), lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS, linker_flags = COMMON_LINKER_FLAGS, deps = [ - ':TestHostResources', ':AsyncDisplayKit-Core', ], frameworks = [ @@ -145,32 +137,32 @@ apple_package( ##################################### apple_resource( name = 'TestsResources', - files = ['AsyncDisplayKitTests/en.lproj/InfoPlist.strings'], - dirs = ['AsyncDisplayKitTests/TestResources'], + files = ['Tests/en.lproj/InfoPlist.strings'], + dirs = ['Tests/TestResources'], ) apple_test( name = 'Tests', test_host_app = ':TestHost', - info_plist = 'AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist', + info_plist = 'Tests/AsyncDisplayKitTests-Info.plist', info_plist_substitutions = { 'PRODUCT_BUNDLE_IDENTIFIER': 'com.facebook.AsyncDisplayKitTests', }, - prefix_header = 'AsyncDisplayKitTests/AsyncDisplayKitTests-Prefix.pch', + prefix_header = 'Tests/AsyncDisplayKitTests-Prefix.pch', + header_path_prefix = 'AsyncDisplayKit', # Expose all ASDK headers to tests - headers = ASYNCDISPLAYKIT_EXPORTED_HEADERS + ASYNCDISPLAYKIT_PRIVATE_HEADERS + glob([ - 'AsyncDisplayKitTests/*.h', - ]), + headers = ASYNCDISPLAYKIT_EXPORTED_HEADERS + ASYNCDISPLAYKIT_PRIVATE_HEADERS + glob(['Tests/*.h']), srcs = glob([ - 'AsyncDisplayKitTests/*.m', - 'AsyncDisplayKitTests/*.mm' + 'Tests/*.m', + 'Tests/*.mm' ], # ASTextNodePerformanceTests are excluded (#2173) - excludes = ['AsyncDisplayKitTests/ASTextNodePerformanceTests.m*'] + excludes = ['Tests/ASTextNodePerformanceTests.m*'] ), - snapshot_reference_images_path='AsyncDisplayKitTests/ReferenceImages', + snapshot_reference_images_path='Tests/ReferenceImages', preprocessor_flags = COMMON_PREPROCESSOR_FLAGS + [ '-Wno-implicit-function-declaration', + '-Wno-deprecated-declarations', ], lang_preprocessor_flags = COMMON_LANG_PREPROCESSOR_FLAGS, linker_flags = COMMON_LINKER_FLAGS, diff --git a/Default-568h@2x.png b/Default-568h@2x.png deleted file mode 100644 index 1547a98454..0000000000 Binary files a/Default-568h@2x.png and /dev/null differ diff --git a/AsyncDisplayKit/ASBlockTypes.h b/Source/ASBlockTypes.h similarity index 100% rename from AsyncDisplayKit/ASBlockTypes.h rename to Source/ASBlockTypes.h diff --git a/AsyncDisplayKit/ASButtonNode.h b/Source/ASButtonNode.h similarity index 100% rename from AsyncDisplayKit/ASButtonNode.h rename to Source/ASButtonNode.h diff --git a/AsyncDisplayKit/ASButtonNode.mm b/Source/ASButtonNode.mm similarity index 100% rename from AsyncDisplayKit/ASButtonNode.mm rename to Source/ASButtonNode.mm diff --git a/AsyncDisplayKit/ASCellNode.h b/Source/ASCellNode.h similarity index 94% rename from AsyncDisplayKit/ASCellNode.h rename to Source/ASCellNode.h index f653ece0b3..87b386e842 100644 --- a/AsyncDisplayKit/ASCellNode.h +++ b/Source/ASCellNode.h @@ -81,6 +81,7 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { * * @return The supplementary element kind, or @c nil if this node does not represent a supplementary element. */ +//TODO change this to be a generic "kind" or "elementKind" that exposes `nil` for row kind @property (nonatomic, copy, readonly, nullable) NSString *supplementaryElementKind; /* @@ -171,16 +172,22 @@ typedef NS_ENUM(NSUInteger, ASCellNodeVisibilityEvent) { */ @property (nonatomic) UITableViewCellSelectionStyle selectionStyle; +/* @abstract The view used as the background of the cell when it is selected. + * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. + * ASCollectionView uses these properties when configuring UICollectionViewCells that host ASCellNodes. + */ +@property (nonatomic, strong, nullable) UIView *selectedBackgroundView; + /* @abstract The accessory type view on the right side of the cell. Please take care of your ASLayoutSpec so that doesn't overlay the accessoryView * @default UITableViewCellAccessoryNone * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. */ @property (nonatomic) UITableViewCellAccessoryType accessoryType; -/* @abstract The seperator inset of the cell seperator line +/* @abstract The inset of the cell separator line * ASTableView uses these properties when configuring UITableViewCells that host ASCellNodes. */ -@property (nonatomic) UIEdgeInsets seperatorInset; +@property (nonatomic) UIEdgeInsets separatorInset; @end diff --git a/AsyncDisplayKit/ASCellNode.mm b/Source/ASCellNode.mm similarity index 93% rename from AsyncDisplayKit/ASCellNode.mm rename to Source/ASCellNode.mm index 64070c142c..bd645d0175 100644 --- a/AsyncDisplayKit/ASCellNode.mm +++ b/Source/ASCellNode.mm @@ -16,6 +16,7 @@ #import #import #import +#import #import #import #import @@ -26,6 +27,7 @@ #import #import +#import #pragma mark - #pragma mark ASCellNode @@ -49,7 +51,6 @@ @implementation ASCellNode @synthesize interactionDelegate = _interactionDelegate; -static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +initialize. - (instancetype)init { @@ -119,12 +120,10 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init _viewControllerNode.frame = self.bounds; } -- (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)newSize +- (void)_locked_rootNodeDidInvalidateSize { - CGSize oldSize = self.bounds.size; - [super _locked_displayNodeDidInvalidateSizeNewSize:newSize]; - if (CGSizeEqualToSize(oldSize, newSize) == NO) { - [self didRelayoutFromOldSize:oldSize toNewSize:newSize]; + if (_interactionDelegate != nil) { + [_interactionDelegate nodeDidInvalidateSize:self]; } } @@ -316,20 +315,25 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init [self handleVisibilityChange:NO]; } -+ (void)initialize ++ (BOOL)requestsVisibilityNotifications { - [super initialize]; - if (ASSubclassOverridesSelector([ASCellNode class], self, @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:))) { - if (__cellClassesForVisibilityNotifications == nil) { - __cellClassesForVisibilityNotifications = [NSMutableSet set]; - } - [__cellClassesForVisibilityNotifications addObject:self]; + static NSCache *cache; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cache = [[NSCache alloc] init]; + }); + NSNumber *result = [cache objectForKey:self]; + if (result == nil) { + BOOL overrides = ASSubclassOverridesSelector([ASCellNode class], self, @selector(cellNodeVisibilityEvent:inScrollView:withCellFrame:)); + result = overrides ? (NSNumber *)kCFBooleanTrue : (NSNumber *)kCFBooleanFalse; + [cache setObject:result forKey:self]; } + return (result == (NSNumber *)kCFBooleanTrue); } - (void)handleVisibilityChange:(BOOL)isVisible { - if ([__cellClassesForVisibilityNotifications containsObject:[self class]] == NO) { + if ([self.class requestsVisibilityNotifications] == NO) { return; // The work below is expensive, and only valuable for subclasses watching visibility events. } @@ -391,6 +395,12 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init return result; } + +- (NSString *)supplementaryElementKind +{ + return self.collectionElement.supplementaryElementKind; +} + @end diff --git a/AsyncDisplayKit/ASCollectionNode+Beta.h b/Source/ASCollectionNode+Beta.h similarity index 100% rename from AsyncDisplayKit/ASCollectionNode+Beta.h rename to Source/ASCollectionNode+Beta.h diff --git a/AsyncDisplayKit/ASCollectionNode.h b/Source/ASCollectionNode.h similarity index 98% rename from AsyncDisplayKit/ASCollectionNode.h rename to Source/ASCollectionNode.h index 2d4c7ece12..67fe74cc25 100644 --- a/AsyncDisplayKit/ASCollectionNode.h +++ b/Source/ASCollectionNode.h @@ -16,6 +16,7 @@ #import #import #import +#import @protocol ASCollectionViewLayoutFacilitatorProtocol; @protocol ASCollectionDelegate; @@ -482,6 +483,15 @@ NS_ASSUME_NONNULL_BEGIN */ - (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForItemAtIndexPath:(NSIndexPath *)indexPath; +/** + * Asks the data source to provide a node-block to display for the given supplementary element in the collection view. + * + * @param collectionNode The sender. + * @param kind The kind of supplementary element. + * @param indexPath The index path of the supplementary element. + */ +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + /** * Asks the data source to provide a node to display for the given supplementary element in the collection view. * diff --git a/AsyncDisplayKit/ASCollectionNode.mm b/Source/ASCollectionNode.mm similarity index 95% rename from AsyncDisplayKit/ASCollectionNode.mm rename to Source/ASCollectionNode.mm index 2d6fb1d046..0bd35b41c8 100644 --- a/AsyncDisplayKit/ASCollectionNode.mm +++ b/Source/ASCollectionNode.mm @@ -13,6 +13,8 @@ #ifndef MINIMAL_ASDK #import +#import +#import #import #import #import @@ -22,9 +24,10 @@ #import #import #import -#import +#import #import #import +#import #pragma mark - _ASCollectionPendingState @@ -43,7 +46,7 @@ { self = [super init]; if (self) { - _rangeMode = ASLayoutRangeModeCount; + _rangeMode = ASLayoutRangeModeUnspecified; _allowsSelection = YES; _allowsMultipleSelection = NO; _inverted = NO; @@ -63,7 +66,7 @@ self = [super init]; if (self) { _tuningParameters = std::vector> (ASLayoutRangeModeCount, std::vector (ASLayoutRangeTypeCount)); - _rangeMode = ASLayoutRangeModeCount; + _rangeMode = ASLayoutRangeModeUnspecified; } return self; } @@ -157,7 +160,7 @@ view.allowsSelection = pendingState.allowsSelection; view.allowsMultipleSelection = pendingState.allowsMultipleSelection; - if (pendingState.rangeMode != ASLayoutRangeModeCount) { + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } } @@ -203,9 +206,9 @@ #pragma mark Setter / Getter // TODO: Implement this without the view. -- (ASCollectionDataController *)dataController +- (ASDataController *)dataController { - return (ASCollectionDataController *)self.view.dataController; + return self.view.dataController; } // TODO: Implement this without the view. @@ -419,13 +422,13 @@ - (NSInteger)numberOfItemsInSection:(NSInteger)section { [self reloadDataInitiallyIfNeeded]; - return [self.dataController numberOfRowsInSection:section]; + return [self.dataController.pendingMap numberOfItemsInSection:section]; } - (NSInteger)numberOfSections { [self reloadDataInitiallyIfNeeded]; - return [self.dataController numberOfSections]; + return self.dataController.pendingMap.numberOfSections; } - (NSArray<__kindof ASCellNode *> *)visibleNodes @@ -437,12 +440,12 @@ - (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath { [self reloadDataInitiallyIfNeeded]; - return [self.dataController nodeAtIndexPath:indexPath]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].node; } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { - return [self.dataController indexPathForNode:cellNode]; + return [self.dataController.pendingMap indexPathForElement:cellNode.collectionElement]; } - (NSArray *)indexPathsForVisibleItems @@ -485,7 +488,7 @@ - (id)contextForSection:(NSInteger)section { ASDisplayNodeAssertMainThread(); - return [self.dataController contextForSection:section]; + return [self.dataController.pendingMap contextForSection:section]; } #pragma mark - Editing diff --git a/AsyncDisplayKit/ASCollectionView.h b/Source/ASCollectionView.h similarity index 97% rename from AsyncDisplayKit/ASCollectionView.h rename to Source/ASCollectionView.h index 3f04cdedd0..b1e8e898fa 100644 --- a/AsyncDisplayKit/ASCollectionView.h +++ b/Source/ASCollectionView.h @@ -12,10 +12,12 @@ #import -#import #import #import #import +#import +#import +#import @class ASCellNode; @class ASCollectionNode; @@ -74,6 +76,20 @@ NS_ASSUME_NONNULL_BEGIN */ - (nullable ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; +/** + * Similar to -indexPathForCell:. + * + * @param cellNode a cellNode in the collection view + * + * @return The index path for this cell node. + * + * @discussion This index path returned by this method is in the _view's_ index space + * and should only be used with @c ASCollectionView directly. To get an index path suitable + * for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the + * collection node instead. + */ +- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT; + /** * Similar to -supplementaryViewForElementKind:atIndexPath: * @@ -113,6 +129,10 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, assign) BOOL inverted; +@end + +@interface ASCollectionView (Deprecated) + /** * Forces the .contentInset to be UIEdgeInsetsZero. * @@ -121,14 +141,7 @@ NS_ASSUME_NONNULL_BEGIN * automaticallyAdjustsScrollViewInsets, which may not be accessible. ASPagerNode uses this to ensure * its flow layout behaves predictably and does not log undefined layout warnings. */ -@property (nonatomic) BOOL zeroContentInsets; - -@end - -@interface ASCollectionView (Deprecated) - -@property (nonatomic, weak) id delegate ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode.delegate instead. If you REALLY want to use this, cast to a UICollectionView but don't rely on the return value."); -@property (nonatomic, weak) id dataSource ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode.dataSource instead. If you REALLY want to use this, cast to a UICollectionView but don't rely on the return value."); +@property (nonatomic) BOOL zeroContentInsets ASDISPLAYNODE_DEPRECATED_MSG("Set automaticallyAdjustsScrollViewInsets=NO on your view controller instead."); /** * The object that acts as the asynchronous delegate of the collection view @@ -396,20 +409,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSArray<__kindof ASCellNode *> *)visibleNodes AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); -/** - * Similar to -indexPathForCell:. - * - * @param cellNode a cellNode in the collection view - * - * @return The index path for this cell node. - * - * @discussion This index path returned by this method is in the _view's_ index space - * and should only be used with @c ASCollectionView directly. To get an index path suitable - * for use with your data source and @c ASCollectionNode, call @c indexPathForNode: on the - * collection node instead. - */ -- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use ASCollectionNode method instead."); - @end ASDISPLAYNODE_DEPRECATED_MSG("Renamed to ASCollectionDataSource.") diff --git a/AsyncDisplayKit/ASCollectionView.mm b/Source/ASCollectionView.mm similarity index 85% rename from AsyncDisplayKit/ASCollectionView.mm rename to Source/ASCollectionView.mm index 8637e16eba..07a866737a 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/Source/ASCollectionView.mm @@ -13,12 +13,14 @@ #import #import #import -#import +#import #import #import #import +#import #import #import +#import #import #import #import @@ -26,6 +28,7 @@ #import #import #import +#import #import #import #import @@ -67,32 +70,31 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma mark - #pragma mark ASCollectionView. -@interface ASCollectionView () { +@interface ASCollectionView () { ASCollectionViewProxy *_proxyDataSource; ASCollectionViewProxy *_proxyDelegate; - ASCollectionDataController *_dataController; + ASDataController *_dataController; ASRangeController *_rangeController; ASCollectionViewLayoutController *_layoutController; id _defaultLayoutInspector; __weak id _layoutInspector; NSMutableSet *_cellsForVisibilityUpdates; + NSMutableSet *_cellsForLayoutUpdates; id _layoutFacilitator; - BOOL _performingBatchUpdates; NSUInteger _superBatchUpdateCount; - NSMutableArray *_batchUpdateBlocks; - BOOL _isDeallocating; ASBatchContext *_batchContext; CGSize _lastBoundsSizeUsedForMeasuringNodes; - BOOL _ignoreNextBoundsSizeChangeForMeasuringNodes; NSMutableSet *_registeredSupplementaryKinds; CGPoint _deceleratingVelocity; + + BOOL _zeroContentInsets; ASCollectionViewInvalidationStyle _nextLayoutInvalidationStyle; @@ -189,6 +191,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; unsigned int collectionNodeNodeForItem:1; unsigned int collectionNodeNodeBlockForItem:1; unsigned int collectionNodeNodeForSupplementaryElement:1; + unsigned int collectionNodeNodeBlockForSupplementaryElement:1; unsigned int collectionNodeSupplementaryElementKindsInSection:1; unsigned int numberOfSectionsInCollectionNode:1; unsigned int collectionNodeNumberOfItemsInSection:1; @@ -203,6 +206,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } _asyncDataSourceFlags; struct { + unsigned int constrainedSizeForSupplementaryNodeOfKindAtIndexPath:1; + unsigned int supplementaryNodesOfKindInSection:1; unsigned int didChangeCollectionViewDataSource:1; unsigned int didChangeCollectionViewDelegate:1; } _layoutInspectorFlags; @@ -255,7 +260,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _rangeController.delegate = self; _rangeController.layoutController = _layoutController; - _dataController = [[ASCollectionDataController alloc] initWithDataSource:self eventLog:eventLog]; + _dataController = [[ASDataController alloc] initWithDataSource:self eventLog:eventLog]; _dataController.delegate = _rangeController; _dataController.environmentDelegate = self; @@ -263,15 +268,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _leadingScreensForBatching = 2.0; - _performingBatchUpdates = NO; - _batchUpdateBlocks = [NSMutableArray array]; - - _superIsPendingDataLoad = YES; - _lastBoundsSizeUsedForMeasuringNodes = self.bounds.size; - // If the initial size is 0, expect a size change very soon which is part of the initial configuration - // and should not trigger a relayout. - _ignoreNextBoundsSizeChangeForMeasuringNodes = CGSizeEqualToSize(_lastBoundsSizeUsedForMeasuringNodes, CGSizeZero); _layoutFacilitator = layoutFacilitator; @@ -284,6 +281,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _registeredSupplementaryKinds = [NSMutableSet set]; _cellsForVisibilityUpdates = [NSMutableSet set]; + _cellsForLayoutUpdates = [NSMutableSet set]; self.backgroundColor = [UIColor whiteColor]; [self registerClass:[_ASCollectionViewCell class] forCellWithReuseIdentifier:kReuseIdentifier]; @@ -315,11 +313,18 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)reloadDataWithCompletion:(void (^)())completion { - ASPerformBlockOnMainThread(^{ - _superIsPendingDataLoad = YES; - [super reloadData]; - }); - [_dataController reloadDataWithAnimationOptions:kASCollectionViewAnimationNone completion:completion]; + ASDisplayNodeAssertMainThread(); + + void (^batchUpdatesCompletion)(BOOL); + if (completion) { + batchUpdatesCompletion = ^(BOOL) { + completion(); + }; + } + + [self performBatchUpdates:^{ + [_changeSet reloadData]; + } completion:batchUpdatesCompletion]; } - (void)reloadData @@ -337,9 +342,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); - _superIsPendingDataLoad = YES; - [_dataController reloadDataImmediatelyWithAnimationOptions:kASCollectionViewAnimationNone]; - [super reloadData]; + [self reloadData]; + [self waitUntilAllUpdatesAreCommitted]; } - (void)relayoutItems @@ -357,6 +361,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } [_dataController waitUntilAllUpdatesAreCommitted]; + + // reloadData of UICollectionView doesn't requery its data source but defers until the next layout pass. + // A forced layout pass is neccessary here to make sure everything is ready after this method returns. + [self layoutIfNeeded]; } - (void)setDataSource:(id)dataSource @@ -367,10 +375,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)setDelegate:(id)delegate { - // The compiler will prevent users from calling this, - // but we will automatically route to @c asyncDelegate - // to support interop with frameworks like TLYShyNavBar. - self.asyncDelegate = (id)delegate; + // Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here. We also allow this when we're doing interop. + ASDisplayNodeAssert(_asyncDelegateFlags.interop || delegate == nil, @"ASCollectionView uses asyncDelegate, not UICollectionView's delegate property."); } - (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy @@ -419,6 +425,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; _asyncDataSourceFlags.collectionNodeNumberOfItemsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:numberOfItemsInSection:)]; _asyncDataSourceFlags.collectionNodeContextForSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:contextForSection:)]; _asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeForSupplementaryElementOfKind:atIndexPath:)]; + _asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement = [_asyncDataSource respondsToSelector:@selector(collectionNode:nodeBlockForSupplementaryElementOfKind:atIndexPath:)]; _asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection = [_asyncDataSource respondsToSelector:@selector(collectionNode:supplementaryElementKindsInSection:)]; _asyncDataSourceFlags.interop = [_asyncDataSource conformsToProtocol:@protocol(ASCollectionDataSourceInterop)]; @@ -555,6 +562,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; { _layoutInspector = layoutInspector; + _layoutInspectorFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath = [_layoutInspector respondsToSelector:@selector(collectionView:constrainedSizeForSupplementaryNodeOfKind:atIndexPath:)]; + _layoutInspectorFlags.supplementaryNodesOfKindInSection = [_layoutInspector respondsToSelector:@selector(collectionView:supplementaryNodesOfKind:inSection:)]; _layoutInspectorFlags.didChangeCollectionViewDataSource = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDataSource:)]; _layoutInspectorFlags.didChangeCollectionViewDelegate = [_layoutInspector respondsToSelector:@selector(didChangeCollectionViewDelegate:)]; @@ -586,19 +595,24 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return [_rangeController tuningParametersForRangeMode:rangeMode rangeType:rangeType]; } +- (void)setZeroContentInsets:(BOOL)zeroContentInsets +{ + _zeroContentInsets = zeroContentInsets; +} + +- (BOOL)zeroContentInsets +{ + return _zeroContentInsets; +} + - (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { return [[self nodeForItemAtIndexPath:indexPath] calculatedSize]; } -- (NSArray *> *)completedNodes -{ - return [_dataController completedNodes]; -} - - (ASCellNode *)nodeForItemAtIndexPath:(NSIndexPath *)indexPath { - return [_dataController nodeAtCompletedIndexPath:indexPath]; + return [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; } - (NSIndexPath *)convertIndexPathFromCollectionNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait @@ -612,11 +626,10 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (indexPath.item == NSNotFound) { return indexPath; } else { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - NSIndexPath *viewIndexPath = [self indexPathForNode:node]; + NSIndexPath *viewIndexPath = [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; if (viewIndexPath == nil && wait) { [self waitUntilAllUpdatesAreCommitted]; - viewIndexPath = [self indexPathForNode:node]; + return [self convertIndexPathFromCollectionNode:indexPath waitingIfNeeded:NO]; } return viewIndexPath; } @@ -658,8 +671,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (indexPath.item == NSNotFound) { return indexPath; } else { - ASCellNode *node = [self nodeForItemAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + return [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; } } @@ -682,12 +694,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (ASCellNode *)supplementaryNodeForElementKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath { - return [_dataController supplementaryNodeOfKind:elementKind atIndexPath:indexPath]; + return [_dataController.visibleMap supplementaryElementOfKind:elementKind atIndexPath:indexPath].node; } - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { - return [self validateIndexPath:[_dataController completedIndexPathForNode:cellNode]]; + return [_dataController.visibleMap indexPathForElement:cellNode.collectionElement]; } - (NSArray *)visibleNodes @@ -755,7 +767,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; [_changeSet addCompletionHandler:completion]; if (_batchUpdateCount == 0) { - [_dataController updateWithChangeSet:_changeSet animated:animated]; + _changeSet.animated = animated; + [_dataController updateWithChangeSet:_changeSet]; _changeSet = nil; } } @@ -821,7 +834,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (id)contextForSection:(NSInteger)section { ASDisplayNodeAssertMainThread(); - return [_dataController contextForSection:section]; + return [_dataController.visibleMap contextForSection:section]; } - (void)insertItemsAtIndexPaths:(NSArray *)indexPaths @@ -864,13 +877,17 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - _superIsPendingDataLoad = NO; - return [_dataController completedNumberOfSections]; + if (_superIsPendingDataLoad) { + [_rangeController setNeedsUpdate]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:1]; + _superIsPendingDataLoad = NO; + } + return _dataController.visibleMap.numberOfSections; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [_dataController completedNumberOfRowsInSection:section]; + return [_dataController.visibleMap numberOfItemsInSection:section]; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath @@ -909,7 +926,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } UICollectionReusableView *view = nil; - ASCellNode *node = [_dataController supplementaryNodeOfKind:kind atIndexPath:indexPath]; + ASCellNode *node = [_dataController.visibleMap supplementaryElementOfKind:kind atIndexPath:indexPath].node; BOOL shouldDequeueExternally = _asyncDataSourceFlags.interopViewForSupplementaryElement && (_asyncDataSourceFlags.interopAlwaysDequeue || node.shouldUseUIKitCell); if (shouldDequeueExternally) { @@ -945,6 +962,7 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (_ASCollectionViewCell *asCell = ASDynamicCast(cell, _ASCollectionViewCell)) { asCell.node = node; + asCell.selectedBackgroundView = node.selectedBackgroundView; [_rangeController configureContentView:cell.contentView forCellNode:node]; } @@ -1330,6 +1348,13 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; - (void)layoutSubviews { + if (_cellsForLayoutUpdates.count > 0) { + NSMutableArray *nodesSizesChanged = [NSMutableArray array]; + [_dataController relayoutNodes:_cellsForLayoutUpdates nodesSizeChanged:nodesSizesChanged]; + [self nodesDidRelayout:nodesSizesChanged]; + } + [_cellsForLayoutUpdates removeAllObjects]; + // Flush any pending invalidation action if needed. ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle; _nextLayoutInvalidationStyle = ASCollectionViewInvalidationStyleNone; @@ -1537,13 +1562,17 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return self.collectionNode; } -#pragma mark - ASCollectionViewDataControllerSource +#pragma mark - ASDataControllerSource optional methods -- (ASCellNode *)dataController:(ASCollectionDataController *)dataController supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)dataController:(ASDataController *)dataController supplementaryNodeBlockOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { + ASCellNodeBlock nodeBlock = nil; ASCellNode *node = nil; - if (_asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement) { - GET_COLLECTIONNODE_OR_RETURN(collectionNode, [[ASCellNode alloc] init] ); + if (_asyncDataSourceFlags.collectionNodeNodeBlockForSupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); + nodeBlock = [_asyncDataSource collectionNode:collectionNode nodeBlockForSupplementaryElementOfKind:kind atIndexPath:indexPath]; + } else if (_asyncDataSourceFlags.collectionNodeNodeForSupplementaryElement) { + GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; }); node = [_asyncDataSource collectionNode:collectionNode nodeForSupplementaryElementOfKind:kind atIndexPath:indexPath]; } else if (_asyncDataSourceFlags.collectionViewNodeForSupplementaryElement) { #pragma clang diagnostic push @@ -1552,22 +1581,29 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma clang diagnostic pop } - if (node == nil && _asyncDataSourceFlags.interop) { - node = [[ASCellNode alloc] init]; - node.shouldUseUIKitCell = YES; + if (nodeBlock == nil) { + if (node) { + nodeBlock = ^{ return node; }; + } else { + BOOL useUIKitCell = _asyncDataSourceFlags.interop; + nodeBlock = ^{ + ASCellNode *node = [[ASCellNode alloc] init]; + node.shouldUseUIKitCell = useUIKitCell; + return node; + }; + } } - - ASDisplayNodeAssert(node != nil, @"A node must be returned for supplementary element of kind '%@' at index path '%@'", kind, indexPath); - return node; + + return nodeBlock; } -- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController sections:(NSIndexSet *)sections +- (NSArray *)dataController:(ASDataController *)dataController supplementaryNodeKindsInSections:(NSIndexSet *)sections { if (_asyncDataSourceFlags.collectionNodeSupplementaryElementKindsInSection) { NSMutableSet *kinds = [NSMutableSet set]; GET_COLLECTIONNODE_OR_RETURN(collectionNode, @[]); [sections enumerateIndexesUsingBlock:^(NSUInteger section, BOOL * _Nonnull stop) { - NSArray *kindsForSection = [_asyncDataSource collectionNode:collectionNode supplementaryElementKindsInSection:section]; + NSArray *kindsForSection = [_asyncDataSource collectionNode:collectionNode supplementaryElementKindsInSection:section]; [kinds addObjectsFromArray:kindsForSection]; }]; return [kinds allObjects]; @@ -1577,18 +1613,28 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } -- (ASSizeRange)dataController:(ASCollectionDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - return [self.layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + if (_layoutInspectorFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath) { + return [self.layoutInspector collectionView:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } + + ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return ASSizeRangeMake(CGSizeZero, CGSizeZero); } -- (NSUInteger)dataController:(ASCollectionDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section +- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section { if (_asyncDataSource == nil) { return 0; } + + if (_layoutInspectorFlags.supplementaryNodesOfKindInSection) { + return [self.layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section]; + } - return [self.layoutInspector collectionView:self supplementaryNodesOfKind:kind inSection:section]; + ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); + return 0; } - (id)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section @@ -1623,6 +1669,11 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return isZeroSized ? @[] : [self indexPathsForVisibleItems]; } +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController +{ + return _dataController.visibleMap; +} + - (ASScrollDirection)scrollDirectionForRangeController:(ASRangeController *)rangeController { return self.scrollDirection; @@ -1639,11 +1690,6 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; return ASInterfaceStateForDisplayNode(self.collectionNode, self.window); } -- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath -{ - return [self nodeForItemAtIndexPath:indexPath]; -} - - (NSString *)nameForRangeControllerDataSource { return self.asyncDataSource ? NSStringFromClass([self.asyncDataSource class]) : NSStringFromClass([self class]); @@ -1651,130 +1697,94 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; #pragma mark - ASRangeControllerDelegate -- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController +- (void)rangeController:(ASRangeController *)rangeController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); - _performingBatchUpdates = YES; + + if (!self.asyncDataSource || _superIsPendingDataLoad) { + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + if (changeSet.includesReloadData) { + //TODO Do we need to notify _layoutFacilitator? + return; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:change.indexPaths batched:YES]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:change.indexSet batched:YES]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:change.indexSet batched:YES]; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:change.indexPaths batched:YES]; + } } -- (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); if (!self.asyncDataSource || _superIsPendingDataLoad) { - if (completion) { - completion(NO); + [changeSet executeCompletionHandlerWithFinished:NO]; + return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes + } + + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ + if(changeSet.includesReloadData) { + _superIsPendingDataLoad = YES; + [super reloadData]; + [changeSet executeCompletionHandlerWithFinished:YES]; + } else { + [_layoutFacilitator collectionViewWillPerformBatchUpdates]; + + __block NSUInteger numberOfUpdates = 0; + [self _superPerformBatchUpdates:^{ + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + [super reloadSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + [super deleteItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + [super deleteSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + [super insertSections:change.indexSet]; + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + [super insertItemsAtIndexPaths:change.indexPaths]; + numberOfUpdates++; + } + } completion:^(BOOL finished){ + // Flush any range changes that happened as part of the update animations ending. + [_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates]; + [changeSet executeCompletionHandlerWithFinished:finished]; + }]; + + // Flush any range changes that happened as part of submitting the update. + [_rangeController updateIfNeeded]; } - _performingBatchUpdates = NO; - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - ASPerformBlockWithoutAnimation(!animated, ^{ - NSUInteger numberOfUpdateBlocks = _batchUpdateBlocks.count; - [_layoutFacilitator collectionViewWillPerformBatchUpdates]; - [self _superPerformBatchUpdates:^{ - for (dispatch_block_t block in _batchUpdateBlocks) { - block(); - } - } completion:^(BOOL finished){ - // Flush any range changes that happened as part of the update animations ending. - [_rangeController updateIfNeeded]; - [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdateBlocks]; - if (completion) { completion(finished); } - }]; - // Flush any range changes that happened as part of submitting the update. - [_rangeController updateIfNeeded]; }); - - [_batchUpdateBlocks removeAllObjects]; - _performingBatchUpdates = NO; -} - -- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - if (!self.asyncDataSource || _superIsPendingDataLoad) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:_performingBatchUpdates]; - if (_performingBatchUpdates) { - [_batchUpdateBlocks addObject:^{ - [super insertItemsAtIndexPaths:indexPaths]; - }]; - } else { - [UIView performWithoutAnimation:^{ - [super insertItemsAtIndexPaths:indexPaths]; - // Flush any range changes that happened as part of submitting the update. - [_rangeController updateIfNeeded]; - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; - }]; - } -} - -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - if (!self.asyncDataSource || _superIsPendingDataLoad) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:indexPaths batched:_performingBatchUpdates]; - if (_performingBatchUpdates) { - [_batchUpdateBlocks addObject:^{ - [super deleteItemsAtIndexPaths:indexPaths]; - }]; - } else { - [UIView performWithoutAnimation:^{ - [super deleteItemsAtIndexPaths:indexPaths]; - // Flush any range changes that happened as part of submitting the update. - [_rangeController updateIfNeeded]; - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; - }]; - } -} - -- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - if (!self.asyncDataSource || _superIsPendingDataLoad) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:_performingBatchUpdates]; - if (_performingBatchUpdates) { - [_batchUpdateBlocks addObject:^{ - [super insertSections:indexSet]; - }]; - } else { - [UIView performWithoutAnimation:^{ - [super insertSections:indexSet]; - // Flush any range changes that happened as part of submitting the update. - [_rangeController updateIfNeeded]; - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; - }]; - } -} - -- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - if (!self.asyncDataSource || _superIsPendingDataLoad) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - [_layoutFacilitator collectionViewWillEditSectionsAtIndexSet:indexSet batched:_performingBatchUpdates]; - if (_performingBatchUpdates) { - [_batchUpdateBlocks addObject:^{ - [super deleteSections:indexSet]; - }]; - } else { - [UIView performWithoutAnimation:^{ - [super deleteSections:indexSet]; - // Flush any range changes that happened as part of submitting the update. - [_rangeController updateIfNeeded]; - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; - }]; - } } #pragma mark - ASCellNodeDelegate @@ -1798,6 +1808,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } } +- (void)nodeDidInvalidateSize:(ASCellNode *)node +{ + [_cellsForLayoutUpdates addObject:node]; + [self setNeedsLayout]; +} + - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged { ASDisplayNodeAssertMainThread(); @@ -1805,36 +1821,53 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; if (!sizeChanged) { return; } + [self nodesDidRelayout:@[node]]; +} + +- (void)nodesDidRelayout:(NSArray *)nodes +{ + ASDisplayNodeAssertMainThread(); - NSIndexPath *uikitIndexPath = [self indexPathForNode:node]; - if (uikitIndexPath == nil) { + if (nodes.count == 0) { return; } - [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:@[ uikitIndexPath ] batched:NO]; - - ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle; - if (invalidationStyle == ASCollectionViewInvalidationStyleNone) { - [self setNeedsLayout]; - invalidationStyle = ASCollectionViewInvalidationStyleWithAnimation; - } - - // If we think we're going to animate, check if this node will prevent it. - if (invalidationStyle == ASCollectionViewInvalidationStyleWithAnimation) { - // TODO: Incorporate `shouldAnimateSizeChanges` into ASEnvironmentState for performance benefit. - static dispatch_once_t onceToken; - static BOOL (^shouldNotAnimateBlock)(ASDisplayNode *); - dispatch_once(&onceToken, ^{ - shouldNotAnimateBlock = ^BOOL(ASDisplayNode * _Nonnull node) { - return (node.shouldAnimateSizeChanges == NO); - }; - }); - if (ASDisplayNodeFindFirstNode(node, shouldNotAnimateBlock) != nil) { - // One single non-animated node causes the whole layout update to be non-animated - invalidationStyle = ASCollectionViewInvalidationStyleWithoutAnimation; + NSMutableArray *uikitIndexPaths = [NSMutableArray arrayWithCapacity:nodes.count]; + for (ASCellNode *node in nodes) { + NSIndexPath *uikitIndexPath = [self indexPathForNode:node]; + if (uikitIndexPath != nil) { + [uikitIndexPaths addObject:uikitIndexPath]; } } + [_layoutFacilitator collectionViewWillEditCellsAtIndexPaths:uikitIndexPaths batched:NO]; + + ASCollectionViewInvalidationStyle invalidationStyle = _nextLayoutInvalidationStyle; + for (ASCellNode *node in nodes) { + if (invalidationStyle == ASCollectionViewInvalidationStyleNone) { + // We nodesDidRelayout also while we are in layoutSubviews. This should be no problem as CA will ignore this + // call while be in a layout pass + [self setNeedsLayout]; + invalidationStyle = ASCollectionViewInvalidationStyleWithAnimation; + } + + // If we think we're going to animate, check if this node will prevent it. + if (invalidationStyle == ASCollectionViewInvalidationStyleWithAnimation) { + // TODO: Incorporate `shouldAnimateSizeChanges` into ASEnvironmentState for performance benefit. + static dispatch_once_t onceToken; + static BOOL (^shouldNotAnimateBlock)(ASDisplayNode *); + dispatch_once(&onceToken, ^{ + shouldNotAnimateBlock = ^BOOL(ASDisplayNode * _Nonnull node) { + return (node.shouldAnimateSizeChanges == NO); + }; + }); + if (ASDisplayNodeFindFirstNode(node, shouldNotAnimateBlock) != nil) { + // One single non-animated node causes the whole layout update to be non-animated + invalidationStyle = ASCollectionViewInvalidationStyleWithoutAnimation; + break; + } + } + } _nextLayoutInvalidationStyle = invalidationStyle; } @@ -1893,39 +1926,22 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier"; } _lastBoundsSizeUsedForMeasuringNodes = newBounds.size; - // First size change occurs during initial configuration. An expensive relayout pass is unnecessary at that time - // and should be avoided, assuming that the initial data loading automatically runs shortly afterward. - if (_ignoreNextBoundsSizeChangeForMeasuringNodes) { - _ignoreNextBoundsSizeChangeForMeasuringNodes = NO; - } else { - // Laying out all nodes is expensive, and performing an empty update may be unsafe - // if the data source has pending changes that it hasn't reported yet – the collection - // view will requery the new counts and expect them to match the previous counts. - // - // We only need to do this if the bounds changed in the non-scrollable direction. - // If, for example, a vertical flow layout has its height changed due to a status bar - // appearance update, we do not need to relayout all nodes. - // For a more permanent fix to the unsafety mentioned above, see https://github.com/facebook/AsyncDisplayKit/pull/2182 - ASScrollDirection scrollDirection = self.scrollableDirections; - BOOL fixedVertically = (ASScrollDirectionContainsVerticalDirection(scrollDirection) == NO); - BOOL fixedHorizontally = (ASScrollDirectionContainsHorizontalDirection(scrollDirection) == NO); + // Laying out all nodes is expensive. + // We only need to do this if the bounds changed in the non-scrollable direction. + // If, for example, a vertical flow layout has its height changed due to a status bar + // appearance update, we do not need to relayout all nodes. + // For a more permanent fix to the unsafety mentioned above, see https://github.com/facebook/AsyncDisplayKit/pull/2182 + ASScrollDirection scrollDirection = self.scrollableDirections; + BOOL fixedVertically = (ASScrollDirectionContainsVerticalDirection(scrollDirection) == NO); + BOOL fixedHorizontally = (ASScrollDirectionContainsHorizontalDirection(scrollDirection) == NO); - BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height); + BOOL changedInNonScrollingDirection = (fixedHorizontally && newBounds.size.width != lastUsedSize.width) || (fixedVertically && newBounds.size.height != lastUsedSize.height); - if (changedInNonScrollingDirection) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // This actually doesn't perform an animation, but prevents the transaction block from being processed in the - // data controller's prevent animation block that would interrupt an interrupted relayout happening in an animation block - // ie. ASCollectionView bounds change on rotation or multi-tasking split view resize. - [self performBatchAnimated:YES updates:^{ - [_dataController relayoutAllNodes]; - } completion:nil]; - // We need to ensure the size requery is done before we update our layout. - [self waitUntilAllUpdatesAreCommitted]; - [self.collectionViewLayout invalidateLayout]; - } -#pragma clang diagnostic pop + if (changedInNonScrollingDirection) { + [_dataController relayoutAllNodes]; + [_dataController waitUntilAllUpdatesAreCommitted]; + // We need to ensure the size requery is done before we update our layout. + [self.collectionViewLayout invalidateLayout]; } } diff --git a/AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h b/Source/ASCollectionViewLayoutFacilitatorProtocol.h similarity index 100% rename from AsyncDisplayKit/ASCollectionViewLayoutFacilitatorProtocol.h rename to Source/ASCollectionViewLayoutFacilitatorProtocol.h diff --git a/AsyncDisplayKit/ASCollectionViewProtocols.h b/Source/ASCollectionViewProtocols.h similarity index 100% rename from AsyncDisplayKit/ASCollectionViewProtocols.h rename to Source/ASCollectionViewProtocols.h diff --git a/AsyncDisplayKit/ASContextTransitioning.h b/Source/ASContextTransitioning.h similarity index 100% rename from AsyncDisplayKit/ASContextTransitioning.h rename to Source/ASContextTransitioning.h diff --git a/AsyncDisplayKit/ASControlNode+Subclasses.h b/Source/ASControlNode+Subclasses.h similarity index 100% rename from AsyncDisplayKit/ASControlNode+Subclasses.h rename to Source/ASControlNode+Subclasses.h diff --git a/AsyncDisplayKit/ASControlNode.h b/Source/ASControlNode.h similarity index 100% rename from AsyncDisplayKit/ASControlNode.h rename to Source/ASControlNode.h diff --git a/AsyncDisplayKit/ASControlNode.mm b/Source/ASControlNode.mm similarity index 100% rename from AsyncDisplayKit/ASControlNode.mm rename to Source/ASControlNode.mm diff --git a/AsyncDisplayKit/ASDisplayNode+Beta.h b/Source/ASDisplayNode+Beta.h similarity index 93% rename from AsyncDisplayKit/ASDisplayNode+Beta.h rename to Source/ASDisplayNode+Beta.h index e34ed968ed..19a1e679ea 100644 --- a/AsyncDisplayKit/ASDisplayNode+Beta.h +++ b/Source/ASDisplayNode+Beta.h @@ -14,7 +14,7 @@ #import #if YOGA -#import + #import YOGA_HEADER_PATH #endif NS_ASSUME_NONNULL_BEGIN @@ -64,7 +64,7 @@ typedef struct { * This property defaults to NO. It will be removed in a future release. */ + (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled."); -+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled.");; ++ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled."); /** * @abstract Recursively ensures node and all subnodes are displayed. @@ -148,7 +148,7 @@ typedef struct { * Note: this has nothing to do with -[CALayer shouldRasterize], which doesn't work with ASDisplayNode's asynchronous * rendering model. */ -@property (nonatomic, assign) BOOL shouldRasterizeDescendants; +@property (nonatomic, assign) BOOL shouldRasterizeDescendants ASDISPLAYNODE_DEPRECATED_MSG("Deprecated in version 2.2"); @end @@ -156,15 +156,19 @@ typedef struct { #if YOGA +extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)); + @interface ASDisplayNode (Yoga) @property (nonatomic, strong) NSArray *yogaChildren; +@property (nonatomic, strong) ASLayout *yogaCalculatedLayout; - (void)addYogaChild:(ASDisplayNode *)child; - (void)removeYogaChild:(ASDisplayNode *)child; -// This method should not normally be called directly. -- (ASLayout *)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize; +// These methods should not normally be called directly. +- (void)invalidateCalculatedYogaLayout; +- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize; @end diff --git a/AsyncDisplayKit/ASDisplayNode+Deprecated.h b/Source/ASDisplayNode+Deprecated.h similarity index 100% rename from AsyncDisplayKit/ASDisplayNode+Deprecated.h rename to Source/ASDisplayNode+Deprecated.h diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/Source/ASDisplayNode+Subclasses.h similarity index 100% rename from AsyncDisplayKit/ASDisplayNode+Subclasses.h rename to Source/ASDisplayNode+Subclasses.h diff --git a/AsyncDisplayKit/ASDisplayNode+Yoga.mm b/Source/ASDisplayNode+Yoga.mm similarity index 83% rename from AsyncDisplayKit/ASDisplayNode+Yoga.mm rename to Source/ASDisplayNode+Yoga.mm index d798e49277..d655a3daf2 100644 --- a/AsyncDisplayKit/ASDisplayNode+Yoga.mm +++ b/Source/ASDisplayNode+Yoga.mm @@ -13,11 +13,12 @@ #import #import #import +#import #import -// If Yoga support becomes a supported feature, move this traversal to ASDisplayNodeExtras. -void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)); -void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)) +#define YOGA_LAYOUT_LOGGING 0 + +extern void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node)) { if (node == nil) { return; @@ -72,6 +73,7 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, YGAlign yogaAlignItems(ASStackLayoutAlignItems alignItems) { switch (alignItems) { + case ASStackLayoutAlignItemsNotSet: return YGAlignAuto; case ASStackLayoutAlignItemsStart: return YGAlignFlexStart; case ASStackLayoutAlignItemsEnd: return YGAlignFlexEnd; case ASStackLayoutAlignItemsCenter: return YGAlignCenter; @@ -262,7 +264,28 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure } } -- (ASLayout *)layoutTreeForYogaNode +- (void)setYogaCalculatedLayout:(ASLayout *)yogaCalculatedLayout +{ + _yogaCalculatedLayout = yogaCalculatedLayout; +} + +- (ASLayout *)yogaCalculatedLayout +{ + return _yogaCalculatedLayout; +} + +- (ASLayout *)layoutForYogaNode +{ + YGNodeRef yogaNode = self.yogaNode; + + CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + CGPoint position = CGPointMake(YGNodeLayoutGetLeft(yogaNode), YGNodeLayoutGetTop(yogaNode)); + + // TODO: If it were possible to set .flattened = YES, it would be valid to do so here. + return [ASLayout layoutWithLayoutElement:self size:size position:position sublayouts:nil]; +} + +- (void)setupYogaCalculatedLayout { YGNodeRef yogaNode = self.yogaNode; // Use property to assign Ref if needed. uint32_t childCount = YGNodeGetChildCount(yogaNode); @@ -271,19 +294,13 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:childCount]; for (ASDisplayNode *subnode in self.yogaChildren) { - [sublayouts addObject:[subnode layoutTreeForYogaNode]]; + [sublayouts addObject:[subnode layoutForYogaNode]]; } - CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); - CGPoint position = CGPointMake(YGNodeLayoutGetLeft(yogaNode), YGNodeLayoutGetTop(yogaNode)); - - ASDisplayNodeLogEvent(self, @"Yoga calculatedSize: %@", NSStringFromCGSize(size)); - - ASLayout *layout = [ASLayout layoutWithLayoutElement:self - size:size - position:position - sublayouts:sublayouts]; - return layout; + // The layout for self should have position CGPointNull, but include the calculated size. + CGSize size = CGSizeMake(YGNodeLayoutGetWidth(yogaNode), YGNodeLayoutGetHeight(yogaNode)); + ASLayout *layout = [ASLayout layoutWithLayoutElement:self size:size sublayouts:sublayouts]; + self.yogaCalculatedLayout = layout; } - (void)setYogaMeasureFuncIfNeeded @@ -298,11 +315,20 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure } } -- (ASLayout *)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize +- (void)invalidateCalculatedYogaLayout +{ + // Yoga internally asserts that this method may only be called on nodes with a measurement function. + YGNodeRef yogaNode = self.yogaNode; + if (YGNodeGetMeasureFunc(yogaNode)) { + YGNodeMarkDirty(yogaNode); + } +} + +- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize { if (ASHierarchyStateIncludesYogaLayoutMeasuring(self.hierarchyState)) { ASDisplayNodeAssert(NO, @"A Yoga layout is being performed by a parent; children must not perform their own until it is done! %@", [self displayNodeRecursiveDescription]); - return [ASLayout layoutWithLayoutElement:self size:CGSizeZero]; + return; } ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { @@ -313,18 +339,10 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure // Apply the constrainedSize as a base, known frame of reference. // If the root node also has style.*Size set, these will be overridden below. + // YGNodeCalculateLayout currently doesn't offer the ability to pass a minimum size (max is passed there). YGNodeStyleSetMinWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.width)); YGNodeStyleSetMinHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.min.height)); - // TODO(appleguy); Is it sufficient to set only Width OR MaxWidth (+ same for height), or do we need all four? - YGNodeStyleSetMaxWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.max.width)); - YGNodeStyleSetMaxHeight(rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.max.height)); - YGNodeStyleSetWidth (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.max.width)); - YGNodeStyleSetHeight (rootYogaNode, yogaFloatForCGFloat(rootConstrainedSize.max.height)); - - // TODO(appleguy): We need this on all containers, not just the root. Need to be able to check for "unset" - YGNodeStyleSetAlignItems(rootYogaNode, yogaAlignItems(self.style.alignItems)); - ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { ASLayoutElementStyle *style = node.style; YGNodeRef yogaNode = node.yogaNode; @@ -337,12 +355,14 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure YGNODE_STYLE_SET_DIMENSION (yogaNode, FlexBasis, style.flexBasis); YGNodeStyleSetFlexDirection (yogaNode, yogaFlexDirection(style.direction)); - YGNodeStyleSetAlignSelf (yogaNode, yogaAlignSelf(style.alignSelf)); - YGNodeStyleSetAlignItems (yogaNode, yogaAlignItems(style.alignItems)); YGNodeStyleSetJustifyContent(yogaNode, yogaJustifyContent(style.justifyContent)); + YGNodeStyleSetAlignSelf (yogaNode, yogaAlignSelf(style.alignSelf)); + ASStackLayoutAlignItems alignItems = style.alignItems; + if (alignItems != ASStackLayoutAlignItemsNotSet) { + YGNodeStyleSetAlignItems(yogaNode, yogaAlignItems(alignItems)); + } YGNodeStyleSetPositionType (yogaNode, style.positionType); - ASEdgeInsets position = style.position; ASEdgeInsets margin = style.margin; ASEdgeInsets padding = style.padding; @@ -389,17 +409,27 @@ YGSize ASLayoutElementYogaMeasureFunc(YGNodeRef yogaNode, float width, YGMeasure yogaFloatForCGFloat(rootConstrainedSize.max.height), YGDirectionInherit); -#if ASEVENTLOG_ENABLE - YGNodePrint(rootYogaNode, (YGPrintOptions)(YGPrintOptionsChildren | YGPrintOptionsStyle | YGPrintOptionsLayout)); -#endif - - ASLayout *yogaLayout = [self layoutTreeForYogaNode]; - ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { + [node setupYogaCalculatedLayout]; node.hierarchyState &= ~ASHierarchyStateYogaLayoutMeasuring; }); - - return yogaLayout; + +#if YOGA_LAYOUT_LOGGING + // Concurrent layouts will interleave the NSLog messages unless we serialize. + // Use @synchornize rather than trampolining to the main thread so the tree state isn't changed. + @synchronized ([ASDisplayNode class]) { + NSLog(@"****************************************************************************"); + NSLog(@"******************** STARTING YOGA -> ASLAYOUT CREATION ********************"); + NSLog(@"****************************************************************************"); + ASDisplayNodePerformBlockOnEveryYogaChild(self, ^(ASDisplayNode * _Nonnull node) { + NSLog(@" "); // Newline + NSLog(@"node = %@", node); + NSLog(@"style = %@", node.style); + NSLog(@"layout = %@", node.yogaCalculatedLayout); + YGNodePrint(node.yogaNode, (YGPrintOptions)(YGPrintOptionsStyle | YGPrintOptionsLayout)); + }); + } +#endif } @end diff --git a/AsyncDisplayKit/ASDisplayNode.h b/Source/ASDisplayNode.h similarity index 100% rename from AsyncDisplayKit/ASDisplayNode.h rename to Source/ASDisplayNode.h diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/Source/ASDisplayNode.mm similarity index 97% rename from AsyncDisplayKit/ASDisplayNode.mm rename to Source/ASDisplayNode.mm index e44f95a34d..ae3c555abe 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/Source/ASDisplayNode.mm @@ -36,6 +36,7 @@ #import #import #import +#import /** * Assert if the current thread owns a mutex. @@ -45,7 +46,7 @@ * and check ownership count of the mutex. */ #if CHECK_LOCKING_SAFETY - #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) ASDisplayNodeAssertFalse(lock.ownedByCurrentThread()); + #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) ASDisplayNodeAssertFalse(lock.ownedByCurrentThread()) #else #define ASDisplayNodeAssertLockUnownedByCurrentThread(lock) #endif @@ -884,7 +885,7 @@ ASLayoutElementFinalLayoutElementDefault return _calculatedDisplayNodeLayout->layout ?: [ASLayout layoutWithLayoutElement:self size:{0, 0}]; } - // Creat a pending display node layout for the layout pass + // Create a pending display node layout for the layout pass _pendingDisplayNodeLayout = std::make_shared( [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize], constrainedSize, @@ -914,34 +915,42 @@ ASLayoutElementFinalLayoutElementDefault if (_pendingDisplayNodeLayout != nullptr) { _pendingDisplayNodeLayout->invalidate(); } + +#if YOGA + [self invalidateCalculatedYogaLayout]; +#endif } - (void)__layout { ASDisplayNodeAssertMainThread(); - ASDN::MutexLocker l(__instanceLock__); - CGRect bounds = _threadSafeBounds; + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); - if (CGRectEqualToRect(bounds, CGRectZero)) { - // Performing layout on a zero-bounds view often results in frame calculations - // with negative sizes after applying margins, which will cause - // measureWithSizeRange: on subnodes to assert. - LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self); - return; - } - - // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as - // this is supposed to happen within the layout transition process - if ([self _isTransitionInProgress]) { - return; - } + { + ASDN::MutexLocker l(__instanceLock__); + CGRect bounds = _threadSafeBounds; - // This method will confirm that the layout is up to date (and update if needed). - // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning). - [self _locked_measureNodeWithBoundsIfNecessary:bounds]; - _pendingDisplayNodeLayout = nullptr; - - [self _locked_layoutPlaceholderIfNecessary]; + if (CGRectEqualToRect(bounds, CGRectZero)) { + // Performing layout on a zero-bounds view often results in frame calculations + // with negative sizes after applying margins, which will cause + // measureWithSizeRange: on subnodes to assert. + LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self); + return; + } + + // If a current layout transition is in progress there is no need to do a measurement and layout pass in here as + // this is supposed to happen within the layout transition process + if (_transitionInProgress) { + return; + } + + // This method will confirm that the layout is up to date (and update if needed). + // Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning). + [self _locked_measureNodeWithBoundsIfNecessary:bounds]; + _pendingDisplayNodeLayout = nullptr; + + [self _locked_layoutPlaceholderIfNecessary]; + } [self layout]; [self layoutDidFinish]; @@ -1057,6 +1066,8 @@ ASLayoutElementFinalLayoutElementDefault - (void)layoutDidFinish { // Hook for subclasses + ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); } #pragma mark Calculation @@ -1080,7 +1091,11 @@ ASLayoutElementFinalLayoutElementDefault if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES && ASHierarchyStateIncludesYogaLayoutMeasuring(_hierarchyState) == NO) { ASDN::MutexUnlocker ul(__instanceLock__); - return [self calculateLayoutFromYogaRoot:constrainedSize]; + [self calculateLayoutFromYogaRoot:constrainedSize]; + } + + if (ASHierarchyStateIncludesYogaLayoutEnabled(_hierarchyState) == YES && self.yogaCalculatedLayout) { + return self.yogaCalculatedLayout; } #endif /* YOGA */ @@ -1259,6 +1274,16 @@ ASLayoutElementFinalLayoutElementDefault return; } + // Let the root node method know that the size was invalidated + [self _locked_rootNodeDidInvalidateSize]; + + __instanceLock__.unlock(); +} + +- (void)_locked_rootNodeDidInvalidateSize +{ + ASDisplayNodeAssertThreadAffinity(self); + // We are the root node and need to re-flow the layout; at least one child needs a new size. CGSize boundsSizeForLayout = ASCeilSizeValues(self.bounds.size); @@ -1278,8 +1303,6 @@ ASLayoutElementFinalLayoutElementDefault // If so, inform our container we need an update (e.g Table, Collection, ViewController, etc). [self _locked_displayNodeDidInvalidateSizeNewSize:layout.size]; } - - __instanceLock__.unlock(); } - (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)size @@ -1307,6 +1330,7 @@ ASLayoutElementFinalLayoutElementDefault - (void)layout { ASDisplayNodeAssertMainThread(); + ASDisplayNodeAssertLockUnownedByCurrentThread(__instanceLock__); __instanceLock__.lock(); if (_calculatedDisplayNodeLayout->isDirty()) { @@ -1320,8 +1344,16 @@ ASLayoutElementFinalLayoutElementDefault - (void)_locked_layoutSublayouts { - for (ASLayout *subnodeLayout in _calculatedDisplayNodeLayout->layout.sublayouts) { - ((ASDisplayNode *)subnodeLayout.layoutElement).frame = subnodeLayout.frame; + ASLayout *layout = _calculatedDisplayNodeLayout->layout; + for (ASDisplayNode *node in _subnodes) { + CGRect frame = [layout frameForElement:node]; + if (CGRectIsNull(frame)) { + // There is no frame for this node in our layout. + // This currently can happen if we get a CA layout pass + // while waiting for the client to run animateLayoutTransition: + } else { + node.frame = frame; + } } } @@ -1479,7 +1511,7 @@ ASLayoutElementFinalLayoutElementDefault - (void)cancelLayoutTransition { ASDN::MutexLocker l(__instanceLock__); - if ([self _isTransitionInProgress]) { + if (_transitionInProgress) { // Cancel transition in progress [self _finishOrCancelTransition]; @@ -2236,7 +2268,7 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; #pragma mark -// We are only the delegate for the layer when we are layer-backed, as UIView performs this funcition normally +// We are only the delegate for the layer when we are layer-backed, as UIView performs this function normally - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event { if (event == kCAOnOrderIn) { @@ -2245,8 +2277,8 @@ static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority"; [self __exitHierarchy]; } - ASDisplayNodeAssert(_flags.layerBacked, @"We shouldn't get called back here if there is no layer"); - return (id)kCFNull; + ASDisplayNodeAssert(_flags.layerBacked, @"We shouldn't get called back here unless we are layer-backed."); + return nil; } #pragma mark - Error Handling @@ -2542,6 +2574,11 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { return; } + if (self.layerBacked && !subnode.layerBacked) { + ASDisplayNodeFailAssert(@"Cannot add a view-backed node as a subnode of a layer-backed node. Supernode: %@, subnode: %@", self, subnode); + return; + } + __instanceLock__.lock(); NSUInteger subnodesCount = _subnodes.count; __instanceLock__.unlock(); @@ -3168,7 +3205,7 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { ASDisplayNodeAssert(_flags.synchronous == NO, @"Node created using -initWithViewBlock:/-initWithLayerBlock: cannot be added to subtree of node with shouldRasterizeDescendants=YES. Node: %@", self); } - // Entered or exited contents rendering state. + // Entered or exited range managed state. if ((newState & ASHierarchyStateRangeManaged) != (oldState & ASHierarchyStateRangeManaged)) { if (newState & ASHierarchyStateRangeManaged) { [self enterInterfaceState:self.supernode.interfaceState]; @@ -3784,6 +3821,20 @@ ASDISPLAYNODE_INLINE BOOL nodeIsInRasterizedTree(ASDisplayNode *node) { [result addObject:@{ @"frameInWindow" : [NSValue valueWithCGRect:windowFrame] }]; } + // Attempt to find view controller. + // Note that the convenience method asdk_associatedViewController has an assertion + // that it's run on main. Since this is a debug method, let's bypass the assertion + // and run up the chain ourselves. + if (_view != nil) { + for (UIResponder *responder in [_view asdk_responderChainEnumerator]) { + UIViewController *vc = ASDynamicCast(responder, UIViewController); + if (vc) { + [result addObject:@{ @"viewController" : ASObjectDescriptionMakeTiny(vc) }]; + break; + } + } + } + if (_view != nil) { [result addObject:@{ @"frame" : [NSValue valueWithCGRect:_view.frame] }]; } else if (_layer != nil) { diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.h b/Source/ASDisplayNodeExtras.h similarity index 100% rename from AsyncDisplayKit/ASDisplayNodeExtras.h rename to Source/ASDisplayNodeExtras.h diff --git a/AsyncDisplayKit/ASDisplayNodeExtras.mm b/Source/ASDisplayNodeExtras.mm similarity index 100% rename from AsyncDisplayKit/ASDisplayNodeExtras.mm rename to Source/ASDisplayNodeExtras.mm diff --git a/AsyncDisplayKit/ASEditableTextNode.h b/Source/ASEditableTextNode.h similarity index 100% rename from AsyncDisplayKit/ASEditableTextNode.h rename to Source/ASEditableTextNode.h diff --git a/AsyncDisplayKit/ASEditableTextNode.mm b/Source/ASEditableTextNode.mm similarity index 100% rename from AsyncDisplayKit/ASEditableTextNode.mm rename to Source/ASEditableTextNode.mm diff --git a/AsyncDisplayKit/ASImageNode+AnimatedImage.mm b/Source/ASImageNode+AnimatedImage.mm similarity index 93% rename from AsyncDisplayKit/ASImageNode+AnimatedImage.mm rename to Source/ASImageNode+AnimatedImage.mm index de30a2a10b..f390097bc8 100644 --- a/AsyncDisplayKit/ASImageNode+AnimatedImage.mm +++ b/Source/ASImageNode+AnimatedImage.mm @@ -20,6 +20,7 @@ #import #import #import +#import #import NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; @@ -91,6 +92,17 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; } if (setCoverImage) { + [self setCoverImage:coverImage]; + } +} + +- (void)setCoverImage:(UIImage *)coverImage +{ + //If we're a network image node, we want to set the default image so + //that it will correctly be restored if it exits the range. + if ([self isKindOfClass:[ASNetworkImageNode class]]) { + [(ASNetworkImageNode *)self setDefaultImage:coverImage]; + } else { self.image = coverImage; } } @@ -172,7 +184,7 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSRunLoopCommonModes; [super didEnterVisibleState]; if (self.animatedImage.coverImageReady) { - self.image = self.animatedImage.coverImage; + [self setCoverImage:self.animatedImage.coverImage]; } [self startAnimating]; } diff --git a/AsyncDisplayKit/ASImageNode.h b/Source/ASImageNode.h similarity index 100% rename from AsyncDisplayKit/ASImageNode.h rename to Source/ASImageNode.h diff --git a/AsyncDisplayKit/ASImageNode.mm b/Source/ASImageNode.mm similarity index 100% rename from AsyncDisplayKit/ASImageNode.mm rename to Source/ASImageNode.mm diff --git a/AsyncDisplayKit/ASMapNode.h b/Source/ASMapNode.h similarity index 100% rename from AsyncDisplayKit/ASMapNode.h rename to Source/ASMapNode.h diff --git a/AsyncDisplayKit/ASMapNode.mm b/Source/ASMapNode.mm similarity index 100% rename from AsyncDisplayKit/ASMapNode.mm rename to Source/ASMapNode.mm diff --git a/AsyncDisplayKit/ASMultiplexImageNode.h b/Source/ASMultiplexImageNode.h similarity index 100% rename from AsyncDisplayKit/ASMultiplexImageNode.h rename to Source/ASMultiplexImageNode.h diff --git a/AsyncDisplayKit/ASMultiplexImageNode.mm b/Source/ASMultiplexImageNode.mm similarity index 100% rename from AsyncDisplayKit/ASMultiplexImageNode.mm rename to Source/ASMultiplexImageNode.mm diff --git a/AsyncDisplayKit/ASNavigationController.h b/Source/ASNavigationController.h similarity index 100% rename from AsyncDisplayKit/ASNavigationController.h rename to Source/ASNavigationController.h diff --git a/AsyncDisplayKit/ASNavigationController.m b/Source/ASNavigationController.m similarity index 100% rename from AsyncDisplayKit/ASNavigationController.m rename to Source/ASNavigationController.m diff --git a/AsyncDisplayKit/ASNetworkImageNode.h b/Source/ASNetworkImageNode.h similarity index 100% rename from AsyncDisplayKit/ASNetworkImageNode.h rename to Source/ASNetworkImageNode.h diff --git a/AsyncDisplayKit/ASNetworkImageNode.mm b/Source/ASNetworkImageNode.mm similarity index 98% rename from AsyncDisplayKit/ASNetworkImageNode.mm rename to Source/ASNetworkImageNode.mm index 760662babf..ba22680e73 100755 --- a/AsyncDisplayKit/ASNetworkImageNode.mm +++ b/Source/ASNetworkImageNode.mm @@ -115,8 +115,10 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; { ASDN::MutexLocker l(__instanceLock__); - _imageWasSetExternally = (image != nil); - if (_imageWasSetExternally) { + BOOL imageWasSetExternally = (image != nil); + BOOL shouldCancelAndClear = imageWasSetExternally && imageWasSetExternally != _imageWasSetExternally; + _imageWasSetExternally = imageWasSetExternally; + if (shouldCancelAndClear) { ASDisplayNodeAssertNil(_URL, @"Directly setting an image on an ASNetworkImageNode causes it to behave like an ASImageNode instead of an ASNetworkImageNode. If this is what you want, set the URL to nil first."); [self _cancelDownloadAndClearImage]; _URL = nil; @@ -513,7 +515,7 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0}; { ASDN::MutexLocker l(__instanceLock__); - if ([_URL isEqual:url]) { + if (ASObjectIsEqual(_URL, url)) { // The download we kicked off is correct, no need to do any more work. _downloadIdentifier = downloadIdentifier; } else { diff --git a/AsyncDisplayKit/ASNodeController+Beta.h b/Source/ASNodeController+Beta.h similarity index 100% rename from AsyncDisplayKit/ASNodeController+Beta.h rename to Source/ASNodeController+Beta.h diff --git a/AsyncDisplayKit/ASNodeController+Beta.m b/Source/ASNodeController+Beta.m similarity index 100% rename from AsyncDisplayKit/ASNodeController+Beta.m rename to Source/ASNodeController+Beta.m diff --git a/AsyncDisplayKit/ASPagerFlowLayout.h b/Source/ASPagerFlowLayout.h similarity index 100% rename from AsyncDisplayKit/ASPagerFlowLayout.h rename to Source/ASPagerFlowLayout.h diff --git a/Source/ASPagerFlowLayout.m b/Source/ASPagerFlowLayout.m new file mode 100644 index 0000000000..86892c9033 --- /dev/null +++ b/Source/ASPagerFlowLayout.m @@ -0,0 +1,104 @@ +// +// ASPagerFlowLayout.m +// AsyncDisplayKit +// +// Created by Levi McCallum on 2/12/16. +// +// 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. +// + +#import +#import +#import + +@interface ASPagerFlowLayout () { + __weak ASCellNode *_currentCellNode; +} + +@end + +@implementation ASPagerFlowLayout + +- (ASCollectionView *)asCollectionView +{ + // Dynamic cast is too slow and not worth it. + return (ASCollectionView *)self.collectionView; +} + +- (void)prepareLayout +{ + [super prepareLayout]; + if (_currentCellNode == nil) { + [self _updateCurrentNode]; + } +} + +- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset +{ + // Don't mess around if the user is interacting with the page node. Although if just a rotation happened we should + // try to use the current index path to not end up setting the target content offset to something in between pages + if (!self.collectionView.decelerating && !self.collectionView.tracking) { + NSIndexPath *indexPath = [self.asCollectionView indexPathForNode:_currentCellNode]; + if (indexPath) { + return [self _targetContentOffsetForItemAtIndexPath:indexPath proposedContentOffset:proposedContentOffset]; + } + } + + return [super targetContentOffsetForProposedContentOffset:proposedContentOffset]; +} + +- (CGPoint)_targetContentOffsetForItemAtIndexPath:(NSIndexPath *)indexPath proposedContentOffset:(CGPoint)proposedContentOffset +{ + if ([self _dataSourceIsEmpty]) { + return proposedContentOffset; + } + + UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; + if (attributes == nil) { + return proposedContentOffset; + } + + CGFloat xOffset = (CGRectGetWidth(self.collectionView.bounds) - CGRectGetWidth(attributes.frame)) / 2.0; + return CGPointMake(attributes.frame.origin.x - xOffset, proposedContentOffset.y); +} + +- (BOOL)_dataSourceIsEmpty +{ + return ([self.collectionView numberOfSections] == 0 || + [self.collectionView numberOfItemsInSection:0] == 0); +} + +- (void)_updateCurrentNode +{ + // Never change node during an animated bounds change (rotation) + // NOTE! Listening for -prepareForAnimatedBoundsChange and -finalizeAnimatedBoundsChange + // isn't sufficient here! It's broken! + NSArray *animKeys = self.collectionView.layer.animationKeys; + for (NSString *key in animKeys) { + if ([key hasPrefix:@"bounds"]) { + return; + } + } + + CGRect bounds = self.collectionView.bounds; + CGRect rect = CGRectMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds), 1, 1); + + NSIndexPath *indexPath = [self layoutAttributesForElementsInRect:rect].firstObject.indexPath; + if (indexPath) { + ASCellNode *node = [self.asCollectionView nodeForItemAtIndexPath:indexPath]; + if (node) { + _currentCellNode = node; + } + } +} + +- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds +{ + [self _updateCurrentNode]; + return [super shouldInvalidateLayoutForBoundsChange:newBounds]; +} + +@end diff --git a/AsyncDisplayKit/ASPagerNode.h b/Source/ASPagerNode.h similarity index 80% rename from AsyncDisplayKit/ASPagerNode.h rename to Source/ASPagerNode.h index 3b314eab00..f4c019cd1a 100644 --- a/AsyncDisplayKit/ASPagerNode.h +++ b/Source/ASPagerNode.h @@ -122,6 +122,24 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSInteger)indexOfPageWithNode:(ASCellNode *)node; +/** + * Tells the pager node to allow its view controller to automatically adjust its content insets. + * + * @see UIViewController.automaticallyAdjustsScrollViewInsets + * + * @discussion ASPagerNode should usually not have its content insets automatically adjusted + * because it scrolls horizontally, and flow layout will log errors because the pages + * do not fit between the top & bottom insets of the collection view. + * + * The default value is NO, which means that ASPagerNode expects that its view controller will + * have automaticallyAdjustsScrollViewInsets=NO. + * + * If this property is NO, but your view controller has automaticallyAdjustsScrollViewInsets=YES, + * the pager node will set the property on the view controller to NO and log a warning message. In the future, + * the pager node will just log the warning, and you'll need to configure your view controller on your own. + */ +@property (nonatomic, assign) BOOL allowsAutomaticInsetsAdjustment; + @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/ASPagerNode.m b/Source/ASPagerNode.m similarity index 88% rename from AsyncDisplayKit/ASPagerNode.m rename to Source/ASPagerNode.m index ce723848b4..a396cc0879 100644 --- a/AsyncDisplayKit/ASPagerNode.m +++ b/Source/ASPagerNode.m @@ -13,11 +13,13 @@ #ifndef MINIMAL_ASDK #import #import +#import #import #import #import #import #import +#import @interface ASPagerNode () { @@ -81,11 +83,6 @@ cv.allowsSelection = NO; cv.showsVerticalScrollIndicator = NO; cv.showsHorizontalScrollIndicator = NO; - - // Zeroing contentInset is important, as UIKit will set the top inset for the navigation bar even though - // our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning. - // From here we cannot disable this directly (UIViewController's automaticallyAdjustsScrollViewInsets). - cv.zeroContentInsets = YES; ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 }; ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; @@ -212,6 +209,23 @@ [self setDelegate:nil]; } +- (void)didEnterVisibleState +{ + [super didEnterVisibleState]; + + // Check that our view controller does not automatically set our content insets + // It would be better to have a -didEnterHierarchy hook to put this in, but + // such a hook doesn't currently exist, and in every use case I can imagine, + // the pager is not hosted inside a range-managed node. + if (_allowsAutomaticInsetsAdjustment == NO) { + UIViewController *vc = [self.view asdk_associatedViewController]; + if (vc.automaticallyAdjustsScrollViewInsets) { + NSLog(@"AsyncDisplayKit: ASPagerNode is setting automaticallyAdjustsScrollViewInsets=NO on its owning view controller %@. This automatic behavior will be disabled in the future. Set allowsAutomaticInsetsAdjustment=YES on the pager node to suppress this behavior.", vc); + vc.automaticallyAdjustsScrollViewInsets = NO; + } + } +} + @end #endif diff --git a/AsyncDisplayKit/ASRunLoopQueue.h b/Source/ASRunLoopQueue.h similarity index 100% rename from AsyncDisplayKit/ASRunLoopQueue.h rename to Source/ASRunLoopQueue.h diff --git a/AsyncDisplayKit/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm similarity index 90% rename from AsyncDisplayKit/ASRunLoopQueue.mm rename to Source/ASRunLoopQueue.mm index 90993a1ebd..ec0990c898 100644 --- a/AsyncDisplayKit/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -19,10 +19,11 @@ #import #define ASRunLoopQueueLoggingEnabled 0 +#define ASRunLoopQueueVerboseLoggingEnabled 0 static void runLoopSourceCallback(void *info) { // No-op -#if ASRunLoopQueueLoggingEnabled +#if ASRunLoopQueueVerboseLoggingEnabled NSLog(@"<%@> - Called runLoopSourceCallback", info); #endif } @@ -65,17 +66,18 @@ static void runLoopSourceCallback(void *info) { // 100ms timer. No resources are wasted in between, as the thread sleeps, and each check is fast. // This time is fast enough for most use cases without excessive churn. CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, -1, 0.1, 0, 0, ^(CFRunLoopTimerRef timer) { + weakSelf->_queueLock.lock(); + if (weakSelf->_queue.size() == 0) { + weakSelf->_queueLock.unlock(); + return; + } + // The scope below is entered while already locked. @autorelease is crucial here; see PR 2890. @autoreleasepool { #if ASRunLoopQueueLoggingEnabled - NSLog(@"ASDeallocQueue Processing: %d objects destroyed", weakSelf->_queue.size()); + NSLog(@"ASDeallocQueue Processing: %lu objects destroyed", weakSelf->_queue.size()); #endif - weakSelf->_queueLock.lock(); - std::deque currentQueue = weakSelf->_queue; - if (currentQueue.size() == 0) { - weakSelf->_queueLock.unlock(); - return; - } // Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing. + std::deque currentQueue = weakSelf->_queue; weakSelf->_queue = std::deque(); weakSelf->_queueLock.unlock(); currentQueue.clear(); @@ -262,9 +264,17 @@ static void runLoopSourceCallback(void *info) { } // itemsToProcess will be empty if _queueConsumer == nil so no need to check again. - auto itemsEnd = itemsToProcess.cend(); - for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) { - _queueConsumer(*iterator, isQueueDrained && iterator == itemsEnd - 1); + if (itemsToProcess.empty() == false) { +#if ASRunloopQueueLoggingEnabled + NSLog(@"<%@> - Starting processing of: %ld", self, itemsToProcess.size()); +#endif + auto itemsEnd = itemsToProcess.cend(); + for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) { + _queueConsumer(*iterator, isQueueDrained && iterator == itemsEnd - 1); +#if ASRunloopQueueLoggingEnabled + NSLog(@"<%@> - Finished processing 1 item", self); +#endif + } } // If the queue is not fully drained yet force another run loop to process next batch of items diff --git a/AsyncDisplayKit/ASScrollNode.h b/Source/ASScrollNode.h similarity index 100% rename from AsyncDisplayKit/ASScrollNode.h rename to Source/ASScrollNode.h diff --git a/AsyncDisplayKit/ASScrollNode.mm b/Source/ASScrollNode.mm similarity index 100% rename from AsyncDisplayKit/ASScrollNode.mm rename to Source/ASScrollNode.mm diff --git a/AsyncDisplayKit/ASSectionController.h b/Source/ASSectionController.h similarity index 100% rename from AsyncDisplayKit/ASSectionController.h rename to Source/ASSectionController.h diff --git a/AsyncDisplayKit/ASSupplementaryNodeSource.h b/Source/ASSupplementaryNodeSource.h similarity index 81% rename from AsyncDisplayKit/ASSupplementaryNodeSource.h rename to Source/ASSupplementaryNodeSource.h index 7ded50e1d4..9d3233c9e2 100644 --- a/AsyncDisplayKit/ASSupplementaryNodeSource.h +++ b/Source/ASSupplementaryNodeSource.h @@ -16,14 +16,14 @@ NS_ASSUME_NONNULL_BEGIN @protocol ASSupplementaryNodeSource /** - * A method to provide the node for the item at the given index. + * A method to provide the node-block for the supplementary element. * * @param elementKind The kind of supplementary element. * @param index The index of the item. - * @return A node for the supplementary element. + * @return A node block for the supplementary element. * @see collectionNode:nodeForSupplementaryElementOfKind:atIndexPath: */ -- (ASCellNode *)nodeForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index; +- (ASCellNodeBlock)nodeBlockForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index; @optional diff --git a/AsyncDisplayKit/ASTabBarController.h b/Source/ASTabBarController.h similarity index 100% rename from AsyncDisplayKit/ASTabBarController.h rename to Source/ASTabBarController.h diff --git a/AsyncDisplayKit/ASTabBarController.m b/Source/ASTabBarController.m similarity index 100% rename from AsyncDisplayKit/ASTabBarController.m rename to Source/ASTabBarController.m diff --git a/AsyncDisplayKit/ASTableNode.h b/Source/ASTableNode.h similarity index 100% rename from AsyncDisplayKit/ASTableNode.h rename to Source/ASTableNode.h diff --git a/AsyncDisplayKit/ASTableNode.mm b/Source/ASTableNode.mm similarity index 97% rename from AsyncDisplayKit/ASTableNode.mm rename to Source/ASTableNode.mm index 12c558e70b..ea7efbd353 100644 --- a/AsyncDisplayKit/ASTableNode.mm +++ b/Source/ASTableNode.mm @@ -12,6 +12,8 @@ #ifndef MINIMAL_ASDK #import +#import +#import #import #import #import @@ -21,6 +23,7 @@ #import #import #import +#import #pragma mark - _ASTablePendingState @@ -40,7 +43,7 @@ { self = [super init]; if (self) { - _rangeMode = ASLayoutRangeModeCount; + _rangeMode = ASLayoutRangeModeUnspecified; _allowsSelection = YES; _allowsSelectionDuringEditing = NO; _allowsMultipleSelection = NO; @@ -125,7 +128,7 @@ view.allowsSelectionDuringEditing = pendingState.allowsSelectionDuringEditing; view.allowsMultipleSelection = pendingState.allowsMultipleSelection; view.allowsMultipleSelectionDuringEditing = pendingState.allowsMultipleSelectionDuringEditing; - if (pendingState.rangeMode != ASLayoutRangeModeCount) { + if (pendingState.rangeMode != ASLayoutRangeModeUnspecified) { [view.rangeController updateCurrentRangeWithMode:pendingState.rangeMode]; } } @@ -439,14 +442,14 @@ ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock) { ASDisplayNodeAssertMainThread(); [self reloadDataInitiallyIfNeeded]; - return [self.dataController numberOfRowsInSection:section]; + return [self.dataController.pendingMap numberOfItemsInSection:section]; } - (NSInteger)numberOfSections { ASDisplayNodeAssertMainThread(); [self reloadDataInitiallyIfNeeded]; - return [self.dataController numberOfSections]; + return [self.dataController.pendingMap numberOfSections]; } - (NSArray<__kindof ASCellNode *> *)visibleNodes @@ -457,13 +460,13 @@ ASLayoutElementCollectionTableSetTraitCollection(_environmentStateLock) - (NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode { - return [self.dataController indexPathForNode:cellNode]; + return [self.dataController.pendingMap indexPathForElement:cellNode.collectionElement]; } - (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath { [self reloadDataInitiallyIfNeeded]; - return [self.dataController nodeAtIndexPath:indexPath]; + return [self.dataController.pendingMap elementForItemAtIndexPath:indexPath].node; } - (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/ASTableView.h b/Source/ASTableView.h similarity index 95% rename from AsyncDisplayKit/ASTableView.h rename to Source/ASTableView.h index 1410ee10c9..3ebb667ffe 100644 --- a/AsyncDisplayKit/ASTableView.h +++ b/Source/ASTableView.h @@ -13,7 +13,6 @@ #import #import -#import #import #import @@ -47,9 +46,11 @@ NS_ASSUME_NONNULL_BEGIN - (nullable ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath AS_WARN_UNUSED_RESULT; /** - * YES to automatically adjust the contentOffset when cells are inserted or deleted "before" - * visible cells, maintaining the users' visible scroll position. Currently this feature tracks insertions, moves and deletions of - * cells, but section edits are ignored. + * YES to automatically adjust the contentOffset when cells are inserted or deleted above + * visible cells, maintaining the users' visible scroll position. + * + * @note This is only applied to non-animated updates. For animated updates, there is no way to + * synchronize or "cancel out" the appearance of a scroll due to UITableView API limitations. * * default is NO. */ @@ -71,9 +72,6 @@ NS_ASSUME_NONNULL_BEGIN @interface ASTableView (Deprecated) -@property (nonatomic, weak) id delegate ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode.delegate instead. If you REALLY want to use this, cast to UITableView but don't rely on the return value."); -@property (nonatomic, weak) id dataSource ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode.dataSource instead. If you REALLY want to use this, cast to UITableView but don't rely on the return value."); - @property (nonatomic, weak) id asyncDelegate ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode's .delegate property instead."); @property (nonatomic, weak) id asyncDataSource ASDISPLAYNODE_DEPRECATED_MSG("Use ASTableNode .dataSource property instead."); diff --git a/AsyncDisplayKit/ASTableView.mm b/Source/ASTableView.mm similarity index 84% rename from AsyncDisplayKit/ASTableView.mm rename to Source/ASTableView.mm index db9e046ac2..7d556edc79 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/Source/ASTableView.mm @@ -18,9 +18,11 @@ #import #import #import +#import #import #import #import +#import #import #import #import @@ -30,7 +32,6 @@ #import #import -static const ASSizeRange kInvalidSizeRange = {CGSizeZero, CGSizeZero}; static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; //#define LOG(...) NSLog(__VA_ARGS__) @@ -79,11 +80,19 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; { _node = node; - self.backgroundColor = node.backgroundColor; - self.selectionStyle = node.selectionStyle; - self.accessoryType = node.accessoryType; - self.separatorInset = node.seperatorInset; - self.clipsToBounds = node.clipsToBounds; + if (node) { + self.backgroundColor = node.backgroundColor; + self.selectionStyle = node.selectionStyle; + self.selectedBackgroundView = node.selectedBackgroundView; + self.separatorInset = node.separatorInset; + self.selectionStyle = node.selectionStyle; + self.accessoryType = node.accessoryType; + + // the following ensures that we clip the entire cell to it's bounds if node.clipsToBounds is set (the default) + // This is actually a workaround for a bug we are seeing in some rare cases (selected background view + // overlaps other cells if size of ASCellNode has changed.) + self.clipsToBounds = node.clipsToBounds; + } [node __setSelectedFromUIKit:self.selected]; [node __setHighlightedFromUIKit:self.highlighted]; @@ -130,8 +139,14 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; NSIndexPath *_pendingVisibleIndexPath; - NSIndexPath *_contentOffsetAdjustmentTopVisibleRow; - CGFloat _contentOffsetAdjustment; + // When we update our data controller in response to an interactive move, + // we don't want to tell the table view about the change (it knows!) + BOOL _updatingInResponseToInteractiveMove; + + // The top cell node that was visible before the update. + __weak ASCellNode *_contentOffsetAdjustmentTopVisibleNode; + // The y-offset of the top visible row's origin before the update. + CGFloat _contentOffsetAdjustmentTopVisibleNodeOffset; CGPoint _deceleratingVelocity; @@ -145,11 +160,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; CALayer *_retainedLayer; CGFloat _nodesConstrainedWidth; - BOOL _ignoreNodesConstrainedWidthChange; BOOL _queuedNodeHeightUpdate; BOOL _isDeallocating; - BOOL _performingBatchUpdates; NSMutableSet *_cellsForVisibilityUpdates; + + BOOL _remeasuringCellNodes; + NSMutableSet *_cellsForLayoutUpdates; // See documentation on same property in ASCollectionView BOOL _hasEverCheckedForBatchFetchingDueToUpdate; @@ -269,9 +285,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _automaticallyAdjustsContentOffset = NO; _nodesConstrainedWidth = self.bounds.size.width; - // If the initial size is 0, expect a size change very soon which is part of the initial configuration - // and should not trigger a relayout. - _ignoreNodesConstrainedWidthChange = (_nodesConstrainedWidth == 0); _proxyDelegate = [[ASTableViewProxy alloc] initWithTarget:nil interceptor:self]; super.delegate = (id)_proxyDelegate; @@ -293,6 +306,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return nil; } _cellsForVisibilityUpdates = [NSMutableSet set]; + _cellsForLayoutUpdates = [NSMutableSet set]; if (!dataControllerClass) { dataControllerClass = [[self class] dataControllerClass]; } @@ -338,10 +352,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)setDelegate:(id)delegate { - // The compiler will prevent users from calling this, - // but we will automatically route to @c asyncDelegate - // to support interop with frameworks like TLYShyNavBar. - self.asyncDelegate = (id)delegate; + // Our UIScrollView superclass sets its delegate to nil on dealloc. Only assert if we get a non-nil value here. + ASDisplayNodeAssert(delegate == nil, @"ASTableView uses asyncDelegate, not UITableView's delegate property."); } - (id)asyncDataSource @@ -358,7 +370,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; // the (common) case of nilling the asyncDataSource in the ViewController's dealloc. In this case our _asyncDataSource // will return as nil (ARC magic) even though the _proxyDataSource still exists. It's really important to hold a strong // reference to the old dataSource in this case because calls to ASTableViewProxy will start failing and cause crashes. - NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = super.dataSource; + NS_VALID_UNTIL_END_OF_SCOPE id oldDataSource = self.dataSource; if (asyncDataSource == nil) { _asyncDataSource = nil; @@ -472,10 +484,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)reloadDataWithCompletion:(void (^)())completion { - ASPerformBlockOnMainThread(^{ - [super reloadData]; - }); - [_dataController reloadDataWithAnimationOptions:UITableViewRowAnimationNone completion:completion]; + ASDisplayNodeAssertMainThread(); + + void (^batchUpdatesCompletion)(BOOL); + if (completion) { + batchUpdatesCompletion = ^(BOOL) { + completion(); + }; + } + + [self beginUpdates]; + [_changeSet reloadData]; + [self endUpdatesWithCompletion:batchUpdatesCompletion]; } - (void)reloadData @@ -486,8 +506,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)reloadDataImmediately { ASDisplayNodeAssertMainThread(); - [_dataController reloadDataImmediatelyWithAnimationOptions:UITableViewRowAnimationNone]; - [super reloadData]; + [self reloadData]; + [_dataController waitUntilAllUpdatesAreCommitted]; } - (void)scrollToRowAtIndexPath:(NSIndexPath *)indexPath atScrollPosition:(UITableViewScrollPosition)scrollPosition animated:(BOOL)animated @@ -527,25 +547,29 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return (ASTableNode *)ASViewToDisplayNode(self); } -- (NSArray *> *)completedNodes +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController { - return [_dataController completedNodes]; + return _dataController.visibleMap; } - (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath { - return [_dataController nodeAtCompletedIndexPath:indexPath]; + return [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; } - (NSIndexPath *)convertIndexPathFromTableNode:(NSIndexPath *)indexPath waitingIfNeeded:(BOOL)wait { // If this is a section index path, we don't currently have a method // to do a mapping. - if (indexPath.row == NSNotFound) { + if (indexPath == nil || indexPath.row == NSNotFound) { return indexPath; } else { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; - return [self indexPathForNode:node waitingIfNeeded:wait]; + NSIndexPath *viewIndexPath = [_dataController.visibleMap convertIndexPath:indexPath fromMap:_dataController.pendingMap]; + if (viewIndexPath == nil) { + [self waitUntilAllUpdatesAreCommitted]; + return [self convertIndexPathFromTableNode:indexPath waitingIfNeeded:NO]; + } + return viewIndexPath; } } @@ -560,8 +584,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (indexPath.row == NSNotFound) { return indexPath; } else { - ASCellNode *node = [self nodeForRowAtIndexPath:indexPath]; - return [_dataController indexPathForNode:node]; + return [_dataController.pendingMap convertIndexPath:indexPath fromMap:_dataController.visibleMap]; } } @@ -618,12 +641,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return nil; } - NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode]; + NSIndexPath *indexPath = [_dataController.visibleMap indexPathForElement:cellNode.collectionElement]; indexPath = [self validateIndexPath:indexPath]; if (indexPath == nil && wait) { [self waitUntilAllUpdatesAreCommitted]; - indexPath = [_dataController completedIndexPathForNode:cellNode]; - indexPath = [self validateIndexPath:indexPath]; + return [self indexPathForNode:cellNode waitingIfNeeded:NO]; } return indexPath; } @@ -658,11 +680,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)endUpdates { - // We capture the current state of whether animations are enabled if they don't provide us with one. - [self endUpdatesAnimated:[UIView areAnimationsEnabled] completion:nil]; + [self endUpdatesWithCompletion:nil]; } -- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion; +- (void)endUpdatesWithCompletion:(void (^)(BOOL completed))completion +{ + // We capture the current state of whether animations are enabled if they don't provide us with one. + [self endUpdatesAnimated:[UIView areAnimationsEnabled] completion:completion]; +} + +- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion { ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertNotNil(_changeSet, @"_changeSet must be available when batch update ends"); @@ -674,7 +701,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; [_changeSet addCompletionHandler:completion]; if (_batchUpdateCount == 0) { - [_dataController updateWithChangeSet:_changeSet animated:animated]; + _changeSet.animated = animated; + [_dataController updateWithChangeSet:_changeSet]; _changeSet = nil; } } @@ -693,21 +721,27 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)layoutSubviews { + // Remeasure all rows if our row width has changed. + _remeasuringCellNodes = YES; CGFloat constrainedWidth = self.bounds.size.width - [self sectionIndexWidth]; - if (_nodesConstrainedWidth != constrainedWidth) { + if (constrainedWidth > 0 && _nodesConstrainedWidth != constrainedWidth) { _nodesConstrainedWidth = constrainedWidth; - // First width change occurs during initial configuration. An expensive relayout pass is unnecessary at that time - // and should be avoided, assuming that the initial data loading automatically runs shortly afterward. - if (_ignoreNodesConstrainedWidthChange) { - _ignoreNodesConstrainedWidthChange = NO; - } else { - [self beginUpdates]; - [_dataController relayoutAllNodes]; - [self endUpdatesAnimated:(ASDisplayNodeLayerHasAnimations(self.layer) == NO) completion:nil]; + [self beginUpdates]; + [_dataController relayoutAllNodes]; + [self endUpdatesAnimated:(ASDisplayNodeLayerHasAnimations(self.layer) == NO) completion:nil]; + } else { + if (_cellsForLayoutUpdates.count > 0) { + NSMutableArray *nodesSizesChanged = [NSMutableArray array]; + [_dataController relayoutNodes:_cellsForLayoutUpdates nodesSizeChanged:nodesSizesChanged]; + if (nodesSizesChanged.count > 0) { + [self requeryNodeHeights]; + } } } - + [_cellsForLayoutUpdates removeAllObjects]; + _remeasuringCellNodes = NO; + // To ensure _nodesConstrainedWidth is up-to-date for every usage, this call to super must be done last [super layoutSubviews]; [_rangeController updateIfNeeded]; @@ -791,52 +825,40 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)beginAdjustingContentOffset { - ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); - _contentOffsetAdjustment = 0; - _contentOffsetAdjustmentTopVisibleRow = self.indexPathsForVisibleRows.firstObject; -} - -- (void)endAdjustingContentOffset -{ - ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); - if (_contentOffsetAdjustment != 0) { - self.contentOffset = CGPointMake(0, self.contentOffset.y+_contentOffsetAdjustment); - } - - _contentOffsetAdjustment = 0; - _contentOffsetAdjustmentTopVisibleRow = nil; -} - -- (void)adjustContentOffsetWithNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths inserting:(BOOL)inserting { - // Maintain the users visible window when inserting or deleting cells by adjusting the content offset for nodes - // before the visible area. If in a begin/end updates block this will update _contentOffsetAdjustment, otherwise it will - // update self.contentOffset directly. - - ASDisplayNodeAssert(_automaticallyAdjustsContentOffset, @"this method should only be called when _automaticallyAdjustsContentOffset == YES"); - - CGFloat dir = (inserting) ? +1 : -1; - CGFloat adjustment = 0; - NSIndexPath *top = _contentOffsetAdjustmentTopVisibleRow ? : self.indexPathsForVisibleRows.firstObject; - - for (int index = 0; index < indexPaths.count; index++) { - NSIndexPath *indexPath = indexPaths[index]; - if ([indexPath compare:top] <= 0) { // if this row is before or equal to the topmost visible row, make adjustments... - ASCellNode *cellNode = nodes[index]; - adjustment += cellNode.calculatedSize.height * dir; - if (indexPath.section == top.section) { - top = [NSIndexPath indexPathForRow:top.row+dir inSection:top.section]; - } + NSIndexPath *firstVisibleIndexPath = [self.indexPathsForVisibleRows sortedArrayUsingSelector:@selector(compare:)].firstObject; + if (firstVisibleIndexPath) { + ASCellNode *node = [self nodeForRowAtIndexPath:firstVisibleIndexPath]; + if (node) { + _contentOffsetAdjustmentTopVisibleNode = node; + _contentOffsetAdjustmentTopVisibleNodeOffset = [self rectForRowAtIndexPath:firstVisibleIndexPath].origin.y - self.bounds.origin.y; } } - - if (_contentOffsetAdjustmentTopVisibleRow) { // true of we are in a begin/end update block (see beginAdjustingContentOffset) - _contentOffsetAdjustmentTopVisibleRow = top; - _contentOffsetAdjustment += adjustment; - } else if (adjustment != 0) { - self.contentOffset = CGPointMake(0, self.contentOffset.y+adjustment); - } } +- (void)endAdjustingContentOffsetAnimated:(BOOL)animated +{ + // We can't do this for animated updates. + if (animated) { + return; + } + + // We can't do this if we didn't have a top visible row before. + if (_contentOffsetAdjustmentTopVisibleNode == nil) { + return; + } + + NSIndexPath *newIndexPathForTopVisibleRow = [self indexPathForNode:_contentOffsetAdjustmentTopVisibleNode]; + // We can't do this if our top visible row was deleted + if (newIndexPathForTopVisibleRow == nil) { + return; + } + + CGFloat newRowOriginYInSelf = [self rectForRowAtIndexPath:newIndexPathForTopVisibleRow].origin.y - self.bounds.origin.y; + CGPoint newContentOffset = self.contentOffset; + newContentOffset.y += (newRowOriginYInSelf - _contentOffsetAdjustmentTopVisibleNodeOffset); + self.contentOffset = newContentOffset; + _contentOffsetAdjustmentTopVisibleNode = nil; +} #pragma mark - Intercepted selectors @@ -861,7 +883,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; _ASTableViewCell *cell = [self dequeueReusableCellWithIdentifier:kCellReuseIdentifier forIndexPath:indexPath]; cell.delegate = self; - ASCellNode *node = [_dataController nodeAtCompletedIndexPath:indexPath]; + ASCellNode *node = [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; if (node) { [_rangeController configureContentView:cell.contentView forCellNode:node]; @@ -873,7 +895,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + ASCellNode *node = [_dataController.visibleMap elementForItemAtIndexPath:indexPath].node; CGFloat height = node.calculatedSize.height; /** @@ -890,12 +912,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return [_dataController completedNumberOfSections]; + return _dataController.visibleMap.numberOfSections; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [_dataController completedNumberOfRowsInSection:section]; + return [_dataController.visibleMap numberOfItemsInSection:section]; } - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath @@ -912,8 +934,18 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; if (_asyncDataSourceFlags.tableViewMoveRow) { [_asyncDataSource tableView:self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; } + // Move node after informing data source in case they call nodeAtIndexPath: - [_dataController moveCompletedNodeAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + // Get up to date + [self waitUntilAllUpdatesAreCommitted]; + // Set our flag to suppress informing super about the change. + _updatingInResponseToInteractiveMove = YES; + // Submit the move + [self moveRowAtIndexPath:sourceIndexPath toIndexPath:destinationIndexPath]; + // Wait for it to finish – should be fast! + [self waitUntilAllUpdatesAreCommitted]; + // Clear the flag + _updatingInResponseToInteractiveMove = NO; } - (void)tableView:(UITableView *)tableView willDisplayCell:(_ASTableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath @@ -1388,11 +1420,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return self.scrollDirection; } -- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath -{ - return [_dataController nodeAtIndexPath:indexPath]; -} - - (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController { ASDisplayNodeAssertMainThread(); @@ -1411,145 +1438,152 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma mark - ASRangeControllerDelegate -- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController +- (void)rangeController:(ASRangeController *)rangeController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); - LOG(@"--- UITableView beginUpdates"); - if (!self.asyncDataSource) { return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - - _performingBatchUpdates = YES; - [super beginUpdates]; - - if (_automaticallyAdjustsContentOffset) { + + if (_automaticallyAdjustsContentOffset && !changeSet.includesReloadData) { [self beginAdjustingContentOffset]; } } -- (void)rangeController:(ASRangeController *)rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); - LOG(@"--- UITableView endUpdates"); - - if (!self.asyncDataSource) { - if (completion) { - completion(NO); - } + if (!self.asyncDataSource || _updatingInResponseToInteractiveMove) { + [changeSet executeCompletionHandlerWithFinished:NO]; return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes } - - if (_automaticallyAdjustsContentOffset) { - [self endAdjustingContentOffset]; + + if (changeSet.includesReloadData) { + LOG(@"UITableView reloadData"); + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadData]"); + } + [super reloadData]; + // Flush any range changes that happened as part of submitting the reload. + [_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:1]; + [changeSet executeCompletionHandlerWithFinished:YES]; + }); + return; + } + + NSUInteger numberOfUpdates = 0; + + LOG(@"--- UITableView beginUpdates"); + [super beginUpdates]; + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView reloadRows:%ld rows", indexPaths.count); + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadRowsAtIndexPaths]: %@", indexPaths); + } + [super reloadRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView reloadSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super reloadSections]: %@", sectionIndexes); + } + [super reloadSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView deleteRows:%ld rows", indexPaths.count); + BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths); + } + [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalDelete]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView deleteSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super deleteSections]: %@", sectionIndexes); + } + [super deleteSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + NSIndexSet *sectionIndexes = change.indexSet; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView insertSections:%@", sectionIndexes); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super insertSections]: %@", sectionIndexes); + } + [super insertSections:sectionIndexes withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeOriginalInsert]) { + NSArray *indexPaths = change.indexPaths; + UITableViewRowAnimation animationOptions = (UITableViewRowAnimation)change.animationOptions; + + LOG(@"UITableView insertRows:%ld rows", indexPaths.count); + BOOL preventAnimation = (animationOptions == UITableViewRowAnimationNone); + ASPerformBlockWithoutAnimation(preventAnimation, ^{ + if (self.test_enableSuperUpdateCallLogging) { + NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths); + } + [super insertRowsAtIndexPaths:indexPaths withRowAnimation:animationOptions]; + }); + + numberOfUpdates++; } - ASPerformBlockWithoutAnimation(!animated, ^{ + LOG(@"--- UITableView endUpdates"); + ASPerformBlockWithoutAnimation(!changeSet.animated, ^{ [super endUpdates]; [_rangeController updateIfNeeded]; + [self _scheduleCheckForBatchFetchingForNumberOfChanges:numberOfUpdates]; }); - - _performingBatchUpdates = NO; - if (completion) { - completion(YES); - } -} - -- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - LOG(@"UITableView insertRows:%ld rows", indexPaths.count); - - if (!self.asyncDataSource) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; - ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super insertRowsAtIndexPaths]: %@", indexPaths); - } - [super insertRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; - if (!_performingBatchUpdates) { - [_rangeController updateIfNeeded]; - } - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; - }); - if (_automaticallyAdjustsContentOffset) { - [self adjustContentOffsetWithNodes:nodes atIndexPaths:indexPaths inserting:YES]; + [self endAdjustingContentOffsetAnimated:changeSet.animated]; } -} - -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - LOG(@"UITableView deleteRows:%ld rows", indexPaths.count); - - if (!self.asyncDataSource) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; - ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super deleteRowsAtIndexPaths]: %@", indexPaths); - } - [super deleteRowsAtIndexPaths:indexPaths withRowAnimation:(UITableViewRowAnimation)animationOptions]; - if (!_performingBatchUpdates) { - [_rangeController updateIfNeeded]; - } - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexPaths.count]; - }); - - if (_automaticallyAdjustsContentOffset) { - [self adjustContentOffsetWithNodes:nodes atIndexPaths:indexPaths inserting:NO]; - } -} - -- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - LOG(@"UITableView insertSections:%@", indexSet); - - - if (!self.asyncDataSource) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; - ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super insertSections]: %@", indexSet); - } - [super insertSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; - if (!_performingBatchUpdates) { - [_rangeController updateIfNeeded]; - } - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; - }); -} - -- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - LOG(@"UITableView deleteSections:%@", indexSet); - - if (!self.asyncDataSource) { - return; // if the asyncDataSource has become invalid while we are processing, ignore this request to avoid crashes - } - - BOOL preventAnimation = animationOptions == UITableViewRowAnimationNone; - ASPerformBlockWithoutAnimation(preventAnimation, ^{ - if (self.test_enableSuperUpdateCallLogging) { - NSLog(@"-[super deleteSections]: %@", indexSet); - } - [super deleteSections:indexSet withRowAnimation:(UITableViewRowAnimation)animationOptions]; - if (!_performingBatchUpdates) { - [_rangeController updateIfNeeded]; - } - [self _scheduleCheckForBatchFetchingForNumberOfChanges:indexSet.count]; - }); + [changeSet executeCompletionHandlerWithFinished:YES]; } #pragma mark - ASDataControllerDelegate @@ -1616,7 +1650,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath { - ASSizeRange constrainedSize = kInvalidSizeRange; + ASSizeRange constrainedSize = ASSizeRangeZero; if (_asyncDelegateFlags.tableNodeConstrainedSizeForRow) { GET_TABLENODE_OR_RETURN(tableNode, constrainedSize); ASSizeRange delegateConstrainedSize = [_asyncDelegate tableNode:tableNode constrainedSizeForRowAtIndexPath:indexPath]; @@ -1717,7 +1751,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)nodeSelectedStateDidChange:(ASCellNode *)node { - NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node]; + NSIndexPath *indexPath = [self indexPathForNode:node]; if (indexPath) { if (node.isSelected) { [self selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; @@ -1729,17 +1763,23 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (void)nodeHighlightedStateDidChange:(ASCellNode *)node { - NSIndexPath *indexPath = [_dataController completedIndexPathForNode:node]; + NSIndexPath *indexPath = [self indexPathForNode:node]; if (indexPath) { [self cellForRowAtIndexPath:indexPath].highlighted = node.isHighlighted; } } +- (void)nodeDidInvalidateSize:(ASCellNode *)node +{ + [_cellsForLayoutUpdates addObject:node]; + [self setNeedsLayout]; +} + - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged { ASDisplayNodeAssertMainThread(); - if (!sizeChanged || _queuedNodeHeightUpdate) { + if (!sizeChanged || _queuedNodeHeightUpdate || _remeasuringCellNodes) { return; } @@ -1796,12 +1836,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; return (indexPath.row == anchor.row+1); // assumes that indexes are valid } else if (indexPath.section > anchor.section && indexPath.row == 0) { - if (anchor.row != [_dataController completedNumberOfRowsInSection:anchor.section] -1) { + if (anchor.row != [self numberOfRowsInSection:anchor.section] -1) { return NO; // anchor is not at the end of the section } NSInteger nextSection = anchor.section+1; - while([_dataController completedNumberOfRowsInSection:nextSection] == 0) { + while([self numberOfRowsInSection:nextSection] == 0) { ++nextSection; } diff --git a/AsyncDisplayKit/ASTableViewInternal.h b/Source/ASTableViewInternal.h similarity index 100% rename from AsyncDisplayKit/ASTableViewInternal.h rename to Source/ASTableViewInternal.h diff --git a/AsyncDisplayKit/ASTableViewProtocols.h b/Source/ASTableViewProtocols.h similarity index 100% rename from AsyncDisplayKit/ASTableViewProtocols.h rename to Source/ASTableViewProtocols.h diff --git a/AsyncDisplayKit/ASTextNode+Beta.h b/Source/ASTextNode+Beta.h similarity index 100% rename from AsyncDisplayKit/ASTextNode+Beta.h rename to Source/ASTextNode+Beta.h diff --git a/AsyncDisplayKit/ASTextNode.h b/Source/ASTextNode.h similarity index 100% rename from AsyncDisplayKit/ASTextNode.h rename to Source/ASTextNode.h diff --git a/AsyncDisplayKit/ASTextNode.mm b/Source/ASTextNode.mm similarity index 99% rename from AsyncDisplayKit/ASTextNode.mm rename to Source/ASTextNode.mm index bdaecee0ee..cbe0dc0bcf 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/Source/ASTextNode.mm @@ -134,6 +134,7 @@ static ASTextKitRenderer *rendererForAttributes(ASTextKitAttributes attributes, NSArray *_exclusionPaths; + NSAttributedString *_attributedText; NSAttributedString *_composedTruncationText; NSString *_highlightedLinkAttributeName; @@ -368,6 +369,12 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; return lineHeight + font.descender; } +- (NSAttributedString *)attributedText +{ + ASDN::MutexLocker l(__instanceLock__); + return _attributedText; +} + - (void)setAttributedText:(NSAttributedString *)attributedText { diff --git a/AsyncDisplayKit/ASVideoNode.h b/Source/ASVideoNode.h similarity index 100% rename from AsyncDisplayKit/ASVideoNode.h rename to Source/ASVideoNode.h diff --git a/AsyncDisplayKit/ASVideoNode.mm b/Source/ASVideoNode.mm similarity index 100% rename from AsyncDisplayKit/ASVideoNode.mm rename to Source/ASVideoNode.mm diff --git a/AsyncDisplayKit/ASVideoPlayerNode.h b/Source/ASVideoPlayerNode.h similarity index 100% rename from AsyncDisplayKit/ASVideoPlayerNode.h rename to Source/ASVideoPlayerNode.h diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/Source/ASVideoPlayerNode.mm similarity index 100% rename from AsyncDisplayKit/ASVideoPlayerNode.mm rename to Source/ASVideoPlayerNode.mm diff --git a/AsyncDisplayKit/ASViewController.h b/Source/ASViewController.h similarity index 84% rename from AsyncDisplayKit/ASViewController.h rename to Source/ASViewController.h index 1c94f912b1..07ee2a997f 100644 --- a/AsyncDisplayKit/ASViewController.h +++ b/Source/ASViewController.h @@ -23,25 +23,34 @@ NS_ASSUME_NONNULL_BEGIN typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitCollectionBlock)(UITraitCollection *traitCollection); typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(CGSize windowSize); +/** + * ASViewController allows you to have a completely node backed hierarchy. It automatically + * handles @c ASVisibilityDepth, automatic range mode and propogating @c ASDisplayTraits to contained nodes. + * + * You can opt-out of node backed hierarchy and use it like a normal UIViewController. + * More importantly, you can use it as a base class for all of your view controllers among which some use a node hierarchy and some don't. + * See examples/ASDKgram project for actual implementation. + */ @interface ASViewController<__covariant DisplayNodeType : ASDisplayNode *> : UIViewController /** - * ASViewController Designated initializer. - * - * @discussion ASViewController allows you to have a completely node backed heirarchy. It automatically - * handles @c ASVisibilityDepth, automatic range mode and propogating @c ASDisplayTraits to contained nodes. + * ASViewController initializer. * * @param node An ASDisplayNode which will provide the root view (self.view) * @return An ASViewController instance whose root view will be backed by the provided ASDisplayNode. * * @see ASVisibilityDepth */ -- (instancetype)initWithNode:(DisplayNodeType)node NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNode:(DisplayNodeType)node; + +NS_ASSUME_NONNULL_END /** * @return node Returns the ASDisplayNode which provides the backing view to the view controller. */ -@property (nonatomic, strong, readonly) DisplayNodeType node; +@property (nonatomic, strong, readonly, null_unspecified) DisplayNodeType node; + +NS_ASSUME_NONNULL_BEGIN /** * Set this block to customize the ASDisplayTraits returned when the VC transitions to the given traitCollection. @@ -93,14 +102,6 @@ typedef ASTraitCollection * _Nonnull (^ASDisplayTraitsForTraitWindowSizeBlock)(C @end -@interface ASViewController (Unavailable) - -- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil AS_UNAVAILABLE("ASViewController requires using -initWithNode:"); - -- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder AS_UNAVAILABLE("ASViewController requires using -initWithNode:"); - -@end - NS_ASSUME_NONNULL_END #endif diff --git a/AsyncDisplayKit/ASViewController.mm b/Source/ASViewController.mm similarity index 93% rename from AsyncDisplayKit/ASViewController.mm rename to Source/ASViewController.mm index fcde40b780..9cd1a50c7c 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/Source/ASViewController.mm @@ -34,14 +34,24 @@ - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { - ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:"); - return [self initWithNode:[[ASDisplayNode alloc] init]]; + if (!(self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { + return nil; + } + + [self _initializeInstance]; + + return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { - ASDisplayNodeAssert(NO, @"ASViewController requires using -initWithNode:"); - return [self initWithNode:[[ASDisplayNode alloc] init]]; + if (!(self = [super initWithCoder:aDecoder])) { + return nil; + } + + [self _initializeInstance]; + + return self; } - (instancetype)initWithNode:(ASDisplayNode *)node @@ -50,10 +60,18 @@ return nil; } - ASDisplayNodeAssertNotNil(node, @"Node must not be nil"); - ASDisplayNodeAssertTrue(!node.layerBacked); _node = node; + [self _initializeInstance]; + return self; +} + +- (void)_initializeInstance +{ + if (_node == nil) { + return; + } + _selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; _nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; _automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol; @@ -63,7 +81,7 @@ // Node already loaded the view [self view]; } else { - // If the node didn't load yet add ourselves as on did load observer to laod the view in case the node gets loaded + // If the node didn't load yet add ourselves as on did load observer to load the view in case the node gets loaded // before the view controller __weak __typeof__(self) weakSelf = self; [_node onDidLoad:^(__kindof ASDisplayNode * _Nonnull node) { @@ -72,8 +90,6 @@ } }]; } - - return self; } - (void)dealloc @@ -83,13 +99,18 @@ - (void)loadView { - ASDisplayNodeAssertTrue(!_node.layerBacked); - // Apple applies a frame and autoresizing masks we need. Allocating a view is not // nearly as expensive as adding and removing it from a hierarchy, and fortunately // we can avoid that here. Enabling layerBacking on a single node in the hierarchy // will have a greater performance benefit than the impact of this transient view. [super loadView]; + + if (_node == nil) { + return; + } + + ASDisplayNodeAssertTrue(!_node.layerBacked); + UIView *view = self.view; CGRect frame = view.frame; UIViewAutoresizing autoresizingMask = view.autoresizingMask; @@ -136,7 +157,7 @@ { if (_ensureDisplayed && self.neverShowPlaceholders) { _ensureDisplayed = NO; - [self.node recursivelyEnsureDisplaySynchronously:YES]; + [_node recursivelyEnsureDisplaySynchronously:YES]; } [super viewDidLayoutSubviews]; } @@ -213,7 +234,9 @@ ASVisibilityDepthImplementation; - (void)updateCurrentRangeModeWithModeIfPossible:(ASLayoutRangeMode)rangeMode { - if (!_automaticallyAdjustRangeModeBasedOnViewEvents) { return; } + if (!_automaticallyAdjustRangeModeBasedOnViewEvents) { + return; + } if (_selfConformsToRangeModeProtocol) { id rangeUpdater = (id)self; @@ -269,7 +292,7 @@ ASVisibilityDepthImplementation; #pragma clang diagnostic ignored "-Wdeprecated-declarations" // Once we've propagated all the traits, layout this node. // Remeasure the node with the latest constrained size – old constrained size may be incorrect. - [self.node layoutThatFits:[self nodeConstrainedSize]]; + [_node layoutThatFits:[self nodeConstrainedSize]]; #pragma clang diagnostic pop } } @@ -287,7 +310,7 @@ ASVisibilityDepthImplementation; { [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; - ASPrimitiveTraitCollection traitCollection = self.node.primitiveTraitCollection; + ASPrimitiveTraitCollection traitCollection = _node.primitiveTraitCollection; traitCollection.containerSize = self.view.bounds.size; [self propagateNewTraitCollection:traitCollection]; } diff --git a/AsyncDisplayKit/ASVisibilityProtocols.h b/Source/ASVisibilityProtocols.h similarity index 100% rename from AsyncDisplayKit/ASVisibilityProtocols.h rename to Source/ASVisibilityProtocols.h diff --git a/AsyncDisplayKit/ASVisibilityProtocols.m b/Source/ASVisibilityProtocols.m similarity index 100% rename from AsyncDisplayKit/ASVisibilityProtocols.m rename to Source/ASVisibilityProtocols.m diff --git a/Source/AsyncDisplayKit+IGListKitMethods.h b/Source/AsyncDisplayKit+IGListKitMethods.h new file mode 100644 index 0000000000..5bc08f613b --- /dev/null +++ b/Source/AsyncDisplayKit+IGListKitMethods.h @@ -0,0 +1,61 @@ +// +// AsyncDisplayKit+IGListKitMethods.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/27/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#if IG_LIST_KIT + +#import +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * If you are using AsyncDisplayKit with IGListKit, you should use + * these methods to provide implementations for methods like + * -cellForItemAtIndex: that don't apply when used with AsyncDisplayKit. + * + * Your section controllers should also conform to @c ASSectionController and your + * supplementary view sources should conform to @c ASSupplementaryNodeSource. + */ + +AS_SUBCLASSING_RESTRICTED +@interface ASIGListSectionControllerMethods : NSObject + +/** + * Call this for your section controller's @c cellForItemAtIndex: method. + */ ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController; + +/** + * Call this for your section controller's @c sizeForItemAtIndex: method. + */ ++ (CGSize)sizeForItemAtIndex:(NSInteger)index; + +@end + +AS_SUBCLASSING_RESTRICTED +@interface ASIGListSupplementaryViewSourceMethods : NSObject + +/** + * Call this for your supplementary source's @c viewForSupplementaryElementOfKind:atIndex: method. + */ ++ (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind + atIndex:(NSInteger)index + sectionController:(IGListSectionController *)sectionController; + +/** + * Call this for your supplementary source's @c sizeForSupplementaryViewOfKind:atIndex: method. + */ ++ (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/Source/AsyncDisplayKit+IGListKitMethods.m b/Source/AsyncDisplayKit+IGListKitMethods.m new file mode 100644 index 0000000000..a4ac764c53 --- /dev/null +++ b/Source/AsyncDisplayKit+IGListKitMethods.m @@ -0,0 +1,47 @@ +// +// AsyncDisplayKit+IGListKitMethods.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/27/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#if IG_LIST_KIT + +#import "AsyncDisplayKit+IGListKitMethods.h" +#import +#import + +@implementation ASIGListSectionControllerMethods + ++ (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index sectionController:(IGListSectionController *)sectionController +{ + return [sectionController.collectionContext dequeueReusableCellOfClass:[_ASCollectionViewCell class] forSectionController:sectionController atIndex:index]; +} + ++ (CGSize)sizeForItemAtIndex:(NSInteger)index +{ + ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); + return CGSizeZero; +} + +@end + +@implementation ASIGListSupplementaryViewSourceMethods + ++ (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind + atIndex:(NSInteger)index + sectionController:(IGListSectionController *)sectionController +{ + return [sectionController.collectionContext dequeueReusableSupplementaryViewOfKind:elementKind forSectionController:sectionController class:[UICollectionReusableView class] atIndex:index]; +} + ++ (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + ASDisplayNodeFailAssert(@"Did not expect %@ to be called.", NSStringFromSelector(_cmd)); + return CGSizeZero; +} + +@end + +#endif // IG_LIST_KIT diff --git a/AsyncDisplayKit/AsyncDisplayKit-Prefix.pch b/Source/AsyncDisplayKit-Prefix.pch similarity index 93% rename from AsyncDisplayKit/AsyncDisplayKit-Prefix.pch rename to Source/AsyncDisplayKit-Prefix.pch index e545805799..374bad0162 100644 --- a/AsyncDisplayKit/AsyncDisplayKit-Prefix.pch +++ b/Source/AsyncDisplayKit-Prefix.pch @@ -22,7 +22,3 @@ #ifndef IG_LIST_KIT #define IG_LIST_KIT __has_include() #endif - -#ifndef YOGA - #define YOGA __has_include() -#endif diff --git a/AsyncDisplayKit/AsyncDisplayKit.h b/Source/AsyncDisplayKit.h similarity index 97% rename from AsyncDisplayKit/AsyncDisplayKit.h rename to Source/AsyncDisplayKit.h index 888a09126a..45c406c869 100644 --- a/AsyncDisplayKit/AsyncDisplayKit.h +++ b/Source/AsyncDisplayKit.h @@ -40,7 +40,7 @@ #import #if IG_LIST_KIT #import -#import +#import #endif #import @@ -82,7 +82,6 @@ #import #import #import -#import #import #import #import @@ -106,6 +105,7 @@ #import #import #import +#import #import #import diff --git a/Base/ASAssert.h b/Source/Base/ASAssert.h similarity index 100% rename from Base/ASAssert.h rename to Source/Base/ASAssert.h diff --git a/Base/ASAvailability.h b/Source/Base/ASAvailability.h similarity index 90% rename from Base/ASAvailability.h rename to Source/Base/ASAvailability.h index c8c0bbc977..48b65ac088 100644 --- a/Base/ASAvailability.h +++ b/Source/Base/ASAvailability.h @@ -27,9 +27,13 @@ #define AS_TARGET_OS_IOS TARGET_OS_IPHONE // If Yoga is available, make it available anywhere we use ASAvailability. -// This reduces Yoga-specific code in other files.// If Yoga is available, make it available anywhere we use ASAvailability. -#if __has_include() -#define YOGA 1 +// This reduces Yoga-specific code in other files. +#ifndef YOGA_HEADER_PATH + #define YOGA_HEADER_PATH +#endif + +#ifndef YOGA + #define YOGA __has_include(YOGA_HEADER_PATH) #endif #if AS_TARGET_OS_OSX diff --git a/Base/ASBaseDefines.h b/Source/Base/ASBaseDefines.h similarity index 100% rename from Base/ASBaseDefines.h rename to Source/Base/ASBaseDefines.h diff --git a/Base/ASEqualityHelpers.h b/Source/Base/ASEqualityHelpers.h similarity index 100% rename from Base/ASEqualityHelpers.h rename to Source/Base/ASEqualityHelpers.h diff --git a/Base/ASLog.h b/Source/Base/ASLog.h similarity index 100% rename from Base/ASLog.h rename to Source/Base/ASLog.h diff --git a/AsyncDisplayKit/AsyncDisplayKit+Debug.h b/Source/Debug/AsyncDisplayKit+Debug.h similarity index 58% rename from AsyncDisplayKit/AsyncDisplayKit+Debug.h rename to Source/Debug/AsyncDisplayKit+Debug.h index 5d9d4d593b..1d0f3fa03c 100644 --- a/AsyncDisplayKit/AsyncDisplayKit+Debug.h +++ b/Source/Debug/AsyncDisplayKit+Debug.h @@ -10,10 +10,8 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import #import #import -#import NS_ASSUME_NONNULL_BEGIN @@ -25,8 +23,7 @@ NS_ASSUME_NONNULL_BEGIN * as well as upscaling (such as providing a URL not suitable for a Retina device). For dev purposes only. * @param enabled Specify YES to show the label on all ASImageNodes with non-1.0x source-to-bounds pixel ratio. */ -+ (void)setShouldShowImageScalingOverlay:(BOOL)show; -+ (BOOL)shouldShowImageScalingOverlay; +@property (class, nonatomic) BOOL shouldShowImageScalingOverlay; @end @@ -41,35 +38,21 @@ NS_ASSUME_NONNULL_BEGIN * overlay rect, but can't be visualized). * @param enable Specify YES to make this debug feature enabled when messaging the ASControlNode class. */ -+ (void)setEnableHitTestDebug:(BOOL)enable; -+ (BOOL)enableHitTestDebug; +@property (class, nonatomic) BOOL enableHitTestDebug; @end #ifndef MINIMAL_ASDK -@interface ASRangeController (Debugging) +@interface ASDisplayNode (RangeDebugging) /** - * Class method to enable a visualization overlay of the all ASRangeController's tuning parameters. For dev purposes only. - * To use, message ASRangeController in the AppDelegate --> [ASRangeController setShouldShowRangeDebugOverlay:YES]; - * @param enable Specify YES to make this debug feature enabled when messaging the ASRangeController class. + * Enable a visualization overlay of the all table/collection tuning parameters. For dev purposes only. + * To use, set this in the AppDelegate --> ASDisplayNode.shouldShowRangeDebugOverlay = YES */ -+ (void)setShouldShowRangeDebugOverlay:(BOOL)show; -+ (BOOL)shouldShowRangeDebugOverlay; - -+ (void)layoutDebugOverlayIfNeeded; - -- (void)addRangeControllerToRangeDebugOverlay; - -- (void)updateRangeController:(ASRangeController *)controller - withScrollableDirections:(ASScrollDirection)scrollableDirections - scrollDirection:(ASScrollDirection)direction - rangeMode:(ASLayoutRangeMode)mode - displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters - preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters - interfaceState:(ASInterfaceState)interfaceState; +@property (class, nonatomic) BOOL shouldShowRangeDebugOverlay; @end #endif + NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/AsyncDisplayKit+Debug.m b/Source/Debug/AsyncDisplayKit+Debug.m similarity index 99% rename from AsyncDisplayKit/AsyncDisplayKit+Debug.m rename to Source/Debug/AsyncDisplayKit+Debug.m index 90170ac1c0..14731c66ff 100644 --- a/AsyncDisplayKit/AsyncDisplayKit+Debug.m +++ b/Source/Debug/AsyncDisplayKit+Debug.m @@ -243,7 +243,7 @@ static BOOL __enableHitTestDebug = NO; static BOOL __shouldShowRangeDebugOverlay = NO; -@implementation ASRangeController (Debugging) +@implementation ASDisplayNode (RangeDebugging) + (void)setShouldShowRangeDebugOverlay:(BOOL)show { @@ -255,6 +255,10 @@ static BOOL __shouldShowRangeDebugOverlay = NO; return __shouldShowRangeDebugOverlay; } +@end + +@implementation ASRangeController (DebugInternal) + + (void)layoutDebugOverlayIfNeeded { [[_ASRangeDebugOverlayView sharedInstance] setNeedsLayout]; @@ -308,7 +312,7 @@ static BOOL __shouldShowRangeDebugOverlay = NO; { static _ASRangeDebugOverlayView *__rangeDebugOverlay = nil; - if (!__rangeDebugOverlay && [ASRangeController shouldShowRangeDebugOverlay]) { + if (!__rangeDebugOverlay && ASDisplayNode.shouldShowRangeDebugOverlay) { __rangeDebugOverlay = [[self alloc] initWithFrame:CGRectZero]; [[self keyWindow] addSubview:__rangeDebugOverlay]; } diff --git a/AsyncDisplayKit/Details/ASAbstractLayoutController.h b/Source/Details/ASAbstractLayoutController.h similarity index 86% rename from AsyncDisplayKit/Details/ASAbstractLayoutController.h rename to Source/Details/ASAbstractLayoutController.h index b7f3fe4c3b..57ec5a1e24 100644 --- a/AsyncDisplayKit/Details/ASAbstractLayoutController.h +++ b/Source/Details/ASAbstractLayoutController.h @@ -15,10 +15,6 @@ NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN -FOUNDATION_EXPORT ASRangeTuningParameters const ASRangeTuningParametersZero; - -FOUNDATION_EXPORT BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs); - FOUNDATION_EXPORT ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters); FOUNDATION_EXPORT ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters); diff --git a/AsyncDisplayKit/Details/ASAbstractLayoutController.mm b/Source/Details/ASAbstractLayoutController.mm similarity index 100% rename from AsyncDisplayKit/Details/ASAbstractLayoutController.mm rename to Source/Details/ASAbstractLayoutController.mm diff --git a/AsyncDisplayKit/Details/ASBasicImageDownloader.h b/Source/Details/ASBasicImageDownloader.h similarity index 100% rename from AsyncDisplayKit/Details/ASBasicImageDownloader.h rename to Source/Details/ASBasicImageDownloader.h diff --git a/AsyncDisplayKit/Details/ASBasicImageDownloader.mm b/Source/Details/ASBasicImageDownloader.mm similarity index 100% rename from AsyncDisplayKit/Details/ASBasicImageDownloader.mm rename to Source/Details/ASBasicImageDownloader.mm diff --git a/AsyncDisplayKit/Details/ASBatchContext.h b/Source/Details/ASBatchContext.h similarity index 100% rename from AsyncDisplayKit/Details/ASBatchContext.h rename to Source/Details/ASBatchContext.h diff --git a/AsyncDisplayKit/Details/ASBatchContext.mm b/Source/Details/ASBatchContext.mm similarity index 100% rename from AsyncDisplayKit/Details/ASBatchContext.mm rename to Source/Details/ASBatchContext.mm diff --git a/AsyncDisplayKit/Details/ASIndexedNodeContext.h b/Source/Details/ASCollectionElement.h similarity index 71% rename from AsyncDisplayKit/Details/ASIndexedNodeContext.h rename to Source/Details/ASCollectionElement.h index 44603d8b9b..483aa3cc64 100644 --- a/AsyncDisplayKit/Details/ASIndexedNodeContext.h +++ b/Source/Details/ASCollectionElement.h @@ -1,5 +1,5 @@ // -// ASIndexedNodeContext.h +// ASCollectionElement.h // AsyncDisplayKit // // Created by Huy Nguyen on 2/28/16. @@ -15,19 +15,15 @@ NS_ASSUME_NONNULL_BEGIN -@interface ASIndexedNodeContext : NSObject +AS_SUBCLASSING_RESTRICTED +@interface ASCollectionElement : NSObject -/** - * The index path at which this node was originally inserted. Don't rely on this - * property too heavily – we should remove it in the future. - */ -@property (nonatomic, readonly, strong) NSIndexPath *indexPath; +//TODO change this to be a generic "kind" or "elementKind" that exposes `nil` for row kind @property (nonatomic, readonly, copy, nullable) NSString *supplementaryElementKind; -@property (nonatomic, readonly, assign) ASSizeRange constrainedSize; +@property (nonatomic, assign) ASSizeRange constrainedSize; @property (weak, nonatomic) id traitEnvironment; - (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock - indexPath:(NSIndexPath *)indexPath supplementaryElementKind:(nullable NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize environment:(id)environment; @@ -43,8 +39,6 @@ NS_ASSUME_NONNULL_BEGIN */ @property (strong, readonly, nullable) ASCellNode *nodeIfAllocated; -+ (NSArray *)indexPathsFromContexts:(NSArray *)contexts; - @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASIndexedNodeContext.mm b/Source/Details/ASCollectionElement.mm similarity index 67% rename from AsyncDisplayKit/Details/ASIndexedNodeContext.mm rename to Source/Details/ASCollectionElement.mm index 0d92dd78fc..2dee84dc27 100644 --- a/AsyncDisplayKit/Details/ASIndexedNodeContext.mm +++ b/Source/Details/ASCollectionElement.mm @@ -1,5 +1,5 @@ // -// ASIndexedNodeContext.mm +// ASCollectionElement.mm // AsyncDisplayKit // // Created by Huy Nguyen on 2/28/16. @@ -9,34 +9,33 @@ // 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. // + #ifndef MINIMAL_ASDK -#import +#import #import #import -@interface ASIndexedNodeContext () +@interface ASCollectionElement () /// Required node block used to allocate a cell node. Nil after the first execution. @property (nonatomic, strong) ASCellNodeBlock nodeBlock; @end -@implementation ASIndexedNodeContext { +@implementation ASCollectionElement { std::mutex _lock; ASCellNode *_node; } - (instancetype)initWithNodeBlock:(ASCellNodeBlock)nodeBlock - indexPath:(NSIndexPath *)indexPath supplementaryElementKind:(nullable NSString *)supplementaryElementKind constrainedSize:(ASSizeRange)constrainedSize environment:(id)environment { - NSAssert(nodeBlock != nil && indexPath != nil, @"Node block and index path must not be nil"); + NSAssert(nodeBlock != nil, @"Node block must not be nil"); self = [super init]; if (self) { _nodeBlock = nodeBlock; - _indexPath = indexPath; _supplementaryElementKind = [supplementaryElementKind copy]; _constrainedSize = constrainedSize; _traitEnvironment = environment; @@ -51,12 +50,11 @@ ASCellNode *node = _nodeBlock(); _nodeBlock = nil; if (node == nil) { - ASDisplayNodeFailAssert(@"Node block returned nil node! Index path: %@", _indexPath); + ASDisplayNodeFailAssert(@"Node block returned nil node!"); node = [[ASCellNode alloc] init]; } - node.cachedIndexPath = _indexPath; - node.supplementaryElementKind = _supplementaryElementKind; node.owningNode = (ASDisplayNode *)_traitEnvironment; + node.collectionElement = self; ASTraitCollectionPropagateDown(node, [_traitEnvironment primitiveTraitCollection]); _node = node; } @@ -69,15 +67,6 @@ return _node; } -+ (NSArray *)indexPathsFromContexts:(NSArray *)contexts -{ - NSMutableArray *result = [NSMutableArray arrayWithCapacity:contexts.count]; - for (ASIndexedNodeContext *ctx in contexts) { - [result addObject:ctx.indexPath]; - } - return result; -} - @end #endif diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.h b/Source/Details/ASCollectionInternal.h similarity index 100% rename from AsyncDisplayKit/Details/ASCollectionInternal.h rename to Source/Details/ASCollectionInternal.h diff --git a/AsyncDisplayKit/Details/ASCollectionInternal.m b/Source/Details/ASCollectionInternal.m similarity index 100% rename from AsyncDisplayKit/Details/ASCollectionInternal.m rename to Source/Details/ASCollectionInternal.m diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h b/Source/Details/ASCollectionViewLayoutController.h similarity index 96% rename from AsyncDisplayKit/Details/ASCollectionViewLayoutController.h rename to Source/Details/ASCollectionViewLayoutController.h index 44544707a2..ccd58d9442 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.h +++ b/Source/Details/ASCollectionViewLayoutController.h @@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @class ASCollectionView; +AS_SUBCLASSING_RESTRICTED @interface ASCollectionViewLayoutController : ASAbstractLayoutController - (instancetype)initWithCollectionView:(ASCollectionView *)collectionView; diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm b/Source/Details/ASCollectionViewLayoutController.mm similarity index 100% rename from AsyncDisplayKit/Details/ASCollectionViewLayoutController.mm rename to Source/Details/ASCollectionViewLayoutController.mm diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.h b/Source/Details/ASCollectionViewLayoutInspector.h similarity index 94% rename from AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.h rename to Source/Details/ASCollectionViewLayoutInspector.h index dd5db70169..8e19d21aaf 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.h +++ b/Source/Details/ASCollectionViewLayoutInspector.h @@ -72,8 +72,9 @@ extern ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *colle /** * A layout inspector for non-flow layouts that returns a constrained size to let the cells layout itself as - * far as possible based on the scrollable direction of the collection view. It throws exceptions for delegate - * methods that are related to supplementary node's management. + * far as possible based on the scrollable direction of the collection view. + * It doesn't support supplementary nodes and therefore doesn't implement delegate methods + * that are related to supplementary node's management. * * @warning This class is not meant to be subclassed and will be restricted in the future. */ diff --git a/AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.m b/Source/Details/ASCollectionViewLayoutInspector.m similarity index 80% rename from AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.m rename to Source/Details/ASCollectionViewLayoutInspector.m index c8deb777ee..aec0b7e5a7 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewLayoutInspector.m +++ b/Source/Details/ASCollectionViewLayoutInspector.m @@ -76,18 +76,6 @@ ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionVi return ASScrollDirectionNone; } -- (ASSizeRange)collectionView:(ASCollectionView *)collectionView constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath -{ - ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return ASSizeRangeMake(CGSizeZero, CGSizeZero); -} - -- (NSUInteger)collectionView:(ASCollectionView *)collectionView supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section -{ - ASDisplayNodeAssert(NO, @"To support supplementary nodes in ASCollectionView, it must have a layoutInspector for layout inspection. (See ASCollectionViewFlowLayoutInspector for an example.)"); - return 0; -} - @end #endif \ No newline at end of file diff --git a/AsyncDisplayKit/Details/ASDataController.h b/Source/Details/ASDataController.h similarity index 61% rename from AsyncDisplayKit/Details/ASDataController.h rename to Source/Details/ASDataController.h index f2c94691b9..02a81a1339 100644 --- a/AsyncDisplayKit/Details/ASDataController.h +++ b/Source/Details/ASDataController.h @@ -29,8 +29,10 @@ NS_ASSUME_NONNULL_BEGIN @class ASCellNode; @class ASDataController; +@class ASElementMap; @class _ASHierarchyChangeSet; @protocol ASTraitEnvironment; +@protocol ASSectionContext; typedef NSUInteger ASDataControllerAnimationOptions; @@ -64,6 +66,18 @@ extern NSString * const ASCollectionInvalidUpdateException; */ - (NSUInteger)numberOfSectionsInDataController:(ASDataController *)dataController; +@optional + +- (NSArray *)dataController:(ASDataController *)dataController supplementaryNodeKindsInSections:(NSIndexSet *)sections; + +- (NSUInteger)dataController:(ASDataController *)dataController supplementaryNodesOfKind:(NSString *)kind inSection:(NSUInteger)section; + +- (ASCellNodeBlock)dataController:(ASDataController *)dataController supplementaryNodeBlockOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (ASSizeRange)dataController:(ASDataController *)dataController constrainedSizeForSupplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; + +- (nullable id)dataController:(ASDataController *)dataController contextForSection:(NSInteger)section; + @end @protocol ASDataControllerEnvironmentDelegate @@ -77,31 +91,18 @@ extern NSString * const ASCollectionInvalidUpdateException; @protocol ASDataControllerDelegate /** - Called for batch update. + * Called before updating with given change set. + * + * @param changeSet The change set that includes all updates */ -- (void)dataControllerBeginUpdates:(ASDataController *)dataController; -- (void)dataControllerWillDeleteAllData:(ASDataController *)dataController; -- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion; +- (void)dataController:(ASDataController *)dataController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; /** - Called for insertion of elements. + * Called for change set updates. + * + * @param changeSet The change set that includes all updates */ -- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - Called for deletion of elements. - */ -- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - Called for insertion of sections. - */ -- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *> *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - Called for deletion of sections. - */ -- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; @end @@ -116,6 +117,16 @@ extern NSString * const ASCollectionInvalidUpdateException; - (instancetype)initWithDataSource:(id)dataSource eventLog:(nullable ASEventLog *)eventLog NS_DESIGNATED_INITIALIZER; +/** + * The map that is currently displayed. The "UIKit index space." + */ +@property (nonatomic, strong, readonly) ASElementMap *visibleMap; + +/** + * The latest map fetched from the data source. May be more recent than @c visibleMap. + */ +@property (nonatomic, strong, readonly) ASElementMap *pendingMap; + /** Data source for fetching data info. */ @@ -164,7 +175,7 @@ extern NSString * const ASCollectionInvalidUpdateException; /** @name Data Updating */ -- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet animated:(BOOL)animated; +- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; /** * Re-measures all loaded nodes in the backing store. @@ -174,52 +185,15 @@ extern NSString * const ASCollectionInvalidUpdateException; */ - (void)relayoutAllNodes; -- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^ _Nullable)())completion; - -- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +/** + * Re-measures given noades in the backing store. + * + * @discussion Used to respond to setNeedsLayout calls in ASCellNode + */ +- (void)relayoutNodes:(id)nodes nodesSizeChanged:(NSMutableArray * _Nonnull)nodesSizesChanged; - (void)waitUntilAllUpdatesAreCommitted; -/** @name Data Querying */ - -- (NSUInteger)numberOfSections; - -- (NSUInteger)numberOfRowsInSection:(NSUInteger)section; - -- (nullable ASCellNode *)nodeAtIndexPath:(NSIndexPath *)indexPath; - -- (NSUInteger)completedNumberOfSections; - -- (NSUInteger)completedNumberOfRowsInSection:(NSUInteger)section; - -- (nullable ASCellNode *)nodeAtCompletedIndexPath:(NSIndexPath *)indexPath; - -/** - * @return The index path, in the data source's index space, for the given node. - */ -- (nullable NSIndexPath *)indexPathForNode:(ASCellNode *)cellNode; - -/** - * @return The index path, in UIKit's index space, for the given node. - * - * @discussion @c indexPathForNode: is returns an index path in the data source's index space. - * This method is useful for e.g. looking up the cell for a given node. - */ -- (nullable NSIndexPath *)completedIndexPathForNode:(ASCellNode *)cellNode; - -/** - * Direct access to the nodes that have completed calculation and layout - */ -- (NSArray *> *)completedNodes; - -/** - * Immediately move this item. This is called by ASTableView when the user has finished an interactive - * item move and the table view is requesting a model update. - * - * This must be called on the main thread. - */ -- (void)moveCompletedNodeAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath; - @end NS_ASSUME_NONNULL_END diff --git a/Source/Details/ASDataController.mm b/Source/Details/ASDataController.mm new file mode 100644 index 0000000000..e6dba245d4 --- /dev/null +++ b/Source/Details/ASDataController.mm @@ -0,0 +1,725 @@ +// +// ASDataController.mm +// AsyncDisplayKit +// +// 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. +// + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +//#define LOG(...) NSLog(__VA_ARGS__) +#define LOG(...) + +#define AS_MEASURE_AVOIDED_DATACONTROLLER_WORK 0 + +#define RETURN_IF_NO_DATASOURCE(val) if (_dataSource == nil) { return val; } +#define ASSERT_ON_EDITING_QUEUE ASDisplayNodeAssertNotNil(dispatch_get_specific(&kASDataControllerEditingQueueKey), @"%@ must be called on the editing transaction queue.", NSStringFromSelector(_cmd)) + +const static NSUInteger kASDataControllerSizingCountPerProcessor = 5; +const static char * kASDataControllerEditingQueueKey = "kASDataControllerEditingQueueKey"; +const static char * kASDataControllerEditingQueueContext = "kASDataControllerEditingQueueContext"; + +NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind"; +NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdateException"; + +typedef void (^ASDataControllerCompletionBlock)(NSArray *elements, NSArray *nodes); + +#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK +@interface ASDataController (AvoidedWorkMeasuring) ++ (void)_didLayoutNode; ++ (void)_expectToInsertNodes:(NSUInteger)count; +@end +#endif + +@interface ASDataController () { + + NSInteger _nextSectionID; + + BOOL _itemCountsFromDataSourceAreValid; // Main thread only. + std::vector _itemCountsFromDataSource; // Main thread only. + + ASMainSerialQueue *_mainSerialQueue; + + dispatch_queue_t _editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes. + dispatch_group_t _editingTransactionGroup; // Group of all edit transaction blocks. Useful for waiting. + + BOOL _initialReloadDataHasBeenCalled; + + struct { + unsigned int supplementaryNodeKindsInSections:1; + unsigned int supplementaryNodesOfKindInSection:1; + unsigned int supplementaryNodeBlockOfKindAtIndexPath:1; + unsigned int constrainedSizeForSupplementaryNodeOfKindAtIndexPath:1; + unsigned int contextForSection:1; + } _dataSourceFlags; +} + +@end + +@implementation ASDataController + +#pragma mark - Lifecycle + +- (instancetype)initWithDataSource:(id)dataSource eventLog:(ASEventLog *)eventLog +{ + if (!(self = [super init])) { + return nil; + } + + _dataSource = dataSource; + + _dataSourceFlags.supplementaryNodeKindsInSections = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodeKindsInSections:)]; + _dataSourceFlags.supplementaryNodesOfKindInSection = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodesOfKind:inSection:)]; + _dataSourceFlags.supplementaryNodeBlockOfKindAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:supplementaryNodeBlockOfKind:atIndexPath:)]; + _dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath = [_dataSource respondsToSelector:@selector(dataController:constrainedSizeForSupplementaryNodeOfKind:atIndexPath:)]; + _dataSourceFlags.contextForSection = [_dataSource respondsToSelector:@selector(dataController:contextForSection:)]; + +#if ASEVENTLOG_ENABLE + _eventLog = eventLog; +#endif + + _visibleMap = _pendingMap = [[ASElementMap alloc] init]; + + _nextSectionID = 0; + + _mainSerialQueue = [[ASMainSerialQueue alloc] init]; + + const char *queueName = [[NSString stringWithFormat:@"org.AsyncDisplayKit.ASDataController.editingTransactionQueue:%p", self] cStringUsingEncoding:NSASCIIStringEncoding]; + _editingTransactionQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL); + dispatch_queue_set_specific(_editingTransactionQueue, &kASDataControllerEditingQueueKey, &kASDataControllerEditingQueueContext, NULL); + _editingTransactionGroup = dispatch_group_create(); + + return self; +} + +- (instancetype)init +{ + ASDisplayNodeFailAssert(@"Failed to call designated initializer."); + id fakeDataSource = nil; + ASEventLog *eventLog = nil; + return [self initWithDataSource:fakeDataSource eventLog:eventLog]; +} + ++ (NSUInteger)parallelProcessorCount +{ + static NSUInteger parallelProcessorCount; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + parallelProcessorCount = [[NSProcessInfo processInfo] activeProcessorCount]; + }); + + return parallelProcessorCount; +} + +#pragma mark - Cell Layout + +- (void)batchLayoutNodesFromContexts:(NSArray *)elements batchSize:(NSInteger)batchSize batchCompletion:(ASDataControllerCompletionBlock)batchCompletionHandler +{ + ASSERT_ON_EDITING_QUEUE; +#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK + [ASDataController _expectToInsertNodes:elements.count]; +#endif + + if (elements.count == 0 || _dataSource == nil) { + batchCompletionHandler(@[], @[]); + return; + } + + ASProfilingSignpostStart(2, _dataSource); + + if (batchSize == 0) { + batchSize = [[ASDataController class] parallelProcessorCount] * kASDataControllerSizingCountPerProcessor; + } + NSUInteger count = elements.count; + + // Processing in batches + for (NSUInteger i = 0; i < count; i += batchSize) { + NSRange batchedRange = NSMakeRange(i, MIN(count - i, batchSize)); + NSArray *batchedContexts = [elements subarrayWithRange:batchedRange]; + NSArray *nodes = [self _layoutNodesFromContexts:batchedContexts]; + batchCompletionHandler(batchedContexts, nodes); + } + + ASProfilingSignpostEnd(2, _dataSource); +} + +/** + * Measure and layout the given node with the constrained size range. + */ +- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize +{ + ASDisplayNodeAssert(ASSizeRangeHasSignificantArea(constrainedSize), @"Attempt to layout cell node with invalid size range %@", NSStringFromASSizeRange(constrainedSize)); + + CGRect frame = CGRectZero; + frame.size = [node layoutThatFits:constrainedSize].size; + node.frame = frame; +} + +- (NSArray *)_layoutNodesFromContexts:(NSArray *)elements +{ + ASSERT_ON_EDITING_QUEUE; + + NSUInteger nodeCount = elements.count; + if (!nodeCount || _dataSource == nil) { + return @[]; + } + + __strong ASCellNode **allocatedNodeBuffer = (__strong ASCellNode **)calloc(nodeCount, sizeof(ASCellNode *)); + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + ASDispatchApply(nodeCount, queue, 0, ^(size_t i) { + RETURN_IF_NO_DATASOURCE(); + + // Allocate the node. + ASCollectionElement *context = elements[i]; + ASCellNode *node = context.node; + if (node == nil) { + ASDisplayNodeAssertNotNil(node, @"Node block created nil node; %@, %@", self, self.dataSource); + node = [[ASCellNode alloc] init]; // Fallback to avoid crash for production apps. + } + + // Layout the node if the size range is valid. + ASSizeRange sizeRange = context.constrainedSize; + if (ASSizeRangeHasSignificantArea(sizeRange)) { + [self _layoutNode:node withConstrainedSize:sizeRange]; + } + +#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK + [ASDataController _didLayoutNode]; +#endif + allocatedNodeBuffer[i] = node; + }); + + BOOL canceled = _dataSource == nil; + + // Create nodes array + NSArray *nodes = canceled ? nil : [NSArray arrayWithObjects:allocatedNodeBuffer count:nodeCount]; + + // Nil out buffer indexes to allow arc to free the stored cells. + for (int i = 0; i < nodeCount; i++) { + allocatedNodeBuffer[i] = nil; + } + free(allocatedNodeBuffer); + + return nodes; +} + +#pragma mark - Data Source Access (Calling _dataSource) + +- (NSArray *)_allIndexPathsForItemsOfKind:(NSString *)kind inSections:(NSIndexSet *)sections +{ + ASDisplayNodeAssertMainThread(); + + if (sections.count == 0 || _dataSource == nil) { + return @[]; + } + + NSMutableArray *indexPaths = [NSMutableArray array]; + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + std::vector counts = [self itemCountsFromDataSource]; + [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) { + NSUInteger itemCount = counts[sectionIndex]; + for (NSUInteger i = 0; i < itemCount; i++) { + [indexPaths addObject:[NSIndexPath indexPathForItem:i inSection:sectionIndex]]; + } + } + }]; + } else if (_dataSourceFlags.supplementaryNodesOfKindInSection) { + [sections enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) { + for (NSUInteger sectionIndex = range.location; sectionIndex < NSMaxRange(range); sectionIndex++) { + NSUInteger itemCount = [_dataSource dataController:self supplementaryNodesOfKind:kind inSection:sectionIndex]; + for (NSUInteger i = 0; i < itemCount; i++) { + [indexPaths addObject:[NSIndexPath indexPathForItem:i inSection:sectionIndex]]; + } + } + }]; + } + + return indexPaths; +} + +/** + * Agressively repopulates supplementary nodes of all kinds for sections that contains some given index paths. + * + * @param map The element map into which to apply the change. + * @param indexPaths The index paths belongs to sections whose supplementary nodes need to be repopulated. + * @param changeSet The changeset that triggered this repopulation. + * @param environment The trait environment needed to initialize elements + * @param indexPathsAreNew YES if index paths are "after the update," NO otherwise. + */ +- (void)_repopulateSupplementaryNodesIntoMap:(ASMutableElementMap *)map + forSectionsContainingIndexPaths:(NSArray *)indexPaths + changeSet:(_ASHierarchyChangeSet *)changeSet + environment:(id)environment + indexPathsAreNew:(BOOL)indexPathsAreNew +{ + ASDisplayNodeAssertMainThread(); + + if (indexPaths.count == 0) { + return; + } + + // Remove all old supplementaries from these sections + NSIndexSet *oldSections = [NSIndexSet as_sectionsFromIndexPaths:indexPaths]; + [map removeSupplementaryElementsInSections:oldSections]; + + // Add in new ones with the new kinds. + NSIndexSet *newSections; + if (indexPathsAreNew) { + newSections = oldSections; + } else { + newSections = [oldSections as_indexesByMapping:^NSUInteger(NSUInteger oldSection) { + return [changeSet newSectionForOldSection:oldSection]; + }]; + } + + for (NSString *kind in [self supplementaryKindsInSections:newSections]) { + [self _insertElementsIntoMap:map kind:kind forSections:newSections environment:environment]; + } +} + +/** + * Inserts new elements of a certain kind for some sections + * + * @param kind The kind of the elements, e.g ASDataControllerRowNodeKind + * @param sections The sections that should be populated by new elements + * @param environment The trait environment needed to initialize elements + */ +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + kind:(NSString *)kind + forSections:(NSIndexSet *)sections + environment:(id)environment +{ + ASDisplayNodeAssertMainThread(); + + if (sections.count == 0 || _dataSource == nil) { + return; + } + + NSArray *indexPaths = [self _allIndexPathsForItemsOfKind:kind inSections:sections]; + [self _insertElementsIntoMap:map kind:kind atIndexPaths:indexPaths environment:environment]; +} + +/** + * Inserts new elements of a certain kind at some index paths + * + * @param map The map to insert the elements into. + * @param kind The kind of the elements, e.g ASDataControllerRowNodeKind + * @param indexPaths The index paths at which new elements should be populated + * @param environment The trait environment needed to initialize elements + */ +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + kind:(NSString *)kind + atIndexPaths:(NSArray *)indexPaths + environment:(id)environment +{ + ASDisplayNodeAssertMainThread(); + + if (indexPaths.count == 0 || _dataSource == nil) { + return; + } + + BOOL isRowKind = [kind isEqualToString:ASDataControllerRowNodeKind]; + if (!isRowKind && !_dataSourceFlags.supplementaryNodeBlockOfKindAtIndexPath) { + // Populating supplementary elements but data source doesn't support. + return; + } + + LOG(@"Populating elements of kind: %@, for index paths: %@", kind, indexPaths); + for (NSIndexPath *indexPath in indexPaths) { + ASCellNodeBlock nodeBlock; + if (isRowKind) { + nodeBlock = [_dataSource dataController:self nodeBlockAtIndexPath:indexPath]; + } else { + nodeBlock = [_dataSource dataController:self supplementaryNodeBlockOfKind:kind atIndexPath:indexPath]; + } + + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + ASCollectionElement *element = [[ASCollectionElement alloc] initWithNodeBlock:nodeBlock + supplementaryElementKind:isRowKind ? nil : kind + constrainedSize:constrainedSize + environment:environment]; + [map insertElement:element atIndexPath:indexPath]; + } +} + +- (void)invalidateDataSourceItemCounts +{ + ASDisplayNodeAssertMainThread(); + _itemCountsFromDataSourceAreValid = NO; +} + +- (std::vector)itemCountsFromDataSource +{ + ASDisplayNodeAssertMainThread(); + if (NO == _itemCountsFromDataSourceAreValid) { + id source = self.dataSource; + NSInteger sectionCount = [source numberOfSectionsInDataController:self]; + std::vector newCounts; + newCounts.reserve(sectionCount); + for (NSInteger i = 0; i < sectionCount; i++) { + newCounts.push_back([source dataController:self rowsInSection:i]); + } + _itemCountsFromDataSource = newCounts; + _itemCountsFromDataSourceAreValid = YES; + } + return _itemCountsFromDataSource; +} + +- (NSArray *)supplementaryKindsInSections:(NSIndexSet *)sections +{ + if (_dataSourceFlags.supplementaryNodeKindsInSections) { + return [_dataSource dataController:self supplementaryNodeKindsInSections:sections]; + } + + return @[]; +} + +- (ASSizeRange)constrainedSizeForNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +{ + ASDisplayNodeAssertMainThread(); + if ([kind isEqualToString:ASDataControllerRowNodeKind]) { + return [_dataSource dataController:self constrainedSizeForNodeAtIndexPath:indexPath]; + } + + if (_dataSourceFlags.constrainedSizeForSupplementaryNodeOfKindAtIndexPath){ + return [_dataSource dataController:self constrainedSizeForSupplementaryNodeOfKind:kind atIndexPath:indexPath]; + } + + ASDisplayNodeAssert(NO, @"Unknown constrained size for node of kind %@ by data source %@", kind, _dataSource); + return ASSizeRangeZero; +} + +#pragma mark - Batching (External API) + +- (void)waitUntilAllUpdatesAreCommitted +{ + ASDisplayNodeAssertMainThread(); + + dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + + // Schedule block in main serial queue to wait until all operations are finished that are + // where scheduled while waiting for the _editingTransactionQueue to finish + [_mainSerialQueue performBlockOnMainThread:^{ }]; +} + +- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet +{ + ASDisplayNodeAssertMainThread(); + + if (changeSet.includesReloadData) { + _initialReloadDataHasBeenCalled = YES; + } + + dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + + /** + * If the initial reloadData has not been called, just bail because we don't have + * our old data source counts. + * See ASUICollectionViewTests.testThatIssuingAnUpdateBeforeInitialReloadIsUnacceptable + * For the issue that UICollectionView has that we're choosing to workaround. + */ + if (!_initialReloadDataHasBeenCalled) { + [changeSet executeCompletionHandlerWithFinished:YES]; + return; + } + + [self invalidateDataSourceItemCounts]; + + ASDataControllerLogEvent(self, @"triggeredUpdate: %@", changeSet); +#if ASEVENTLOG_ENABLE + NSString *changeSetDescription = ASObjectDescriptionMakeTiny(changeSet); + [changeSet addCompletionHandler:^(BOOL finished) { + ASDataControllerLogEvent(self, @"finishedUpdate: %@", changeSetDescription); + }]; +#endif + + // Attempt to mark the update completed. This is when update validation will occur inside the changeset. + // If an invalid update exception is thrown, we catch it and inject our "validationErrorSource" object, + // which is the table/collection node's data source, into the exception reason to help debugging. + @try { + [changeSet markCompletedWithNewItemCounts:[self itemCountsFromDataSource]]; + } @catch (NSException *e) { + id responsibleDataSource = self.validationErrorSource; + if (e.name == ASCollectionInvalidUpdateException && responsibleDataSource != nil) { + [NSException raise:ASCollectionInvalidUpdateException format:@"%@: %@", [responsibleDataSource class], e.reason]; + } else { + @throw e; + } + } + + // Mutable copy of current data. + ASMutableElementMap *mutableMap = [_pendingMap mutableCopy]; + + // Step 1: update the mutable copies to match the data source's state + [self _updateSectionContextsInMap:mutableMap changeSet:changeSet]; + //TODO If _elements is the same, use a fast path + [self _updateElementsInMap:mutableMap changeSet:changeSet]; + + // Step 2: Clone the new data + ASElementMap *newMap = [mutableMap copy]; + + _pendingMap = newMap; + + dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{ + // Step 3: Layout **all** new elements without batching in background. + NSArray *unmeasuredElements = [ASDataController unmeasuredElementsFromMap:newMap]; + // TODO layout in batches, esp reloads + [self batchLayoutNodesFromContexts:unmeasuredElements batchSize:unmeasuredElements.count batchCompletion:^(id, id) { + ASSERT_ON_EDITING_QUEUE; + [_mainSerialQueue performBlockOnMainThread:^{ + [_delegate dataController:self willUpdateWithChangeSet:changeSet]; + + // Step 4: Deploy the new data as "completed" and inform delegate + _visibleMap = newMap; + + [_delegate dataController:self didUpdateWithChangeSet:changeSet]; + }]; + }]; + }); +} + +/** + * Update sections based on the given change set. + */ +- (void)_updateSectionContextsInMap:(ASMutableElementMap *)map changeSet:(_ASHierarchyChangeSet *)changeSet +{ + ASDisplayNodeAssertMainThread(); + + if (!_dataSourceFlags.contextForSection) { + return; + } + + // TODO if the change set includes solely section reloads that together are equivalent to reloadData (i.e reload the only section), + // do a reloadData here as an optimization. + + if (changeSet.includesReloadData) { + + [map removeAllSectionContexts]; + + NSUInteger sectionCount = [self itemCountsFromDataSource].size(); + NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]; + [self _insertSectionContextsIntoMap:map indexes:sectionIndexes]; + // Return immediately because reloadData can't be used in conjuntion with other updates. + return; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + [map removeSectionContextsAtIndexes:change.indexSet]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertSectionContextsIntoMap:map indexes:change.indexSet]; + } +} + +- (void)_insertSectionContextsIntoMap:(ASMutableElementMap *)map indexes:(NSIndexSet *)sectionIndexes +{ + ASDisplayNodeAssertMainThread(); + + if (!_dataSourceFlags.contextForSection) { + return; + } + + [sectionIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + id context = [_dataSource dataController:self contextForSection:idx]; + ASSection *section = [[ASSection alloc] initWithSectionID:_nextSectionID context:context]; + [map insertSection:section atIndex:idx]; + _nextSectionID++; + }]; +} + +/** + * Update elements based on the given change set. + */ +- (void)_updateElementsInMap:(ASMutableElementMap *)map changeSet:(_ASHierarchyChangeSet *)changeSet +{ + ASDisplayNodeAssertMainThread(); + + __weak id environment = [self.environmentDelegate dataControllerEnvironment]; + + // TODO if the change set includes solely section reloads that together are equivalent to reloadData (i.e reload the only section), + // do a reloadData here as an optimization. + + if (changeSet.includesReloadData) { + [map removeAllElements]; + + NSUInteger sectionCount = [self itemCountsFromDataSource].size(); + if (sectionCount > 0) { + NSIndexSet *sectionIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, sectionCount)]; + [self _insertElementsIntoMap:map sections:sectionIndexes environment:environment]; + } + // Return immediately because reloadData can't be used in conjuntion with other updates. + return; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) { + [map removeItemsAtIndexPaths:change.indexPaths]; + // Aggressively repopulate supplementary nodes (#1773 & #1629) + [self _repopulateSupplementaryNodesIntoMap:map forSectionsContainingIndexPaths:change.indexPaths + changeSet:changeSet + environment:environment + indexPathsAreNew:NO]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) { + NSIndexSet *sectionIndexes = change.indexSet; + [map removeSupplementaryElementsInSections:sectionIndexes]; + [map removeSectionsOfItems:sectionIndexes]; + } + + for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertElementsIntoMap:map sections:change.indexSet environment:environment]; + } + + for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) { + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind atIndexPaths:change.indexPaths environment:environment]; + // Aggressively reload supplementary nodes (#1773 & #1629) + [self _repopulateSupplementaryNodesIntoMap:map forSectionsContainingIndexPaths:change.indexPaths + changeSet:changeSet + environment:environment + indexPathsAreNew:YES]; + } +} + +- (void)_insertElementsIntoMap:(ASMutableElementMap *)map + sections:(NSIndexSet *)sectionIndexes + environment:(id)environment +{ + ASDisplayNodeAssertMainThread(); + + if (sectionIndexes.count == 0 || _dataSource == nil) { + return; + } + + // Items + [map insertEmptySectionsOfItemsAtIndexes:sectionIndexes]; + [self _insertElementsIntoMap:map kind:ASDataControllerRowNodeKind forSections:sectionIndexes environment:environment]; + + // Supplementaries + for (NSString *kind in [self supplementaryKindsInSections:sectionIndexes]) { + // Step 2: Populate new elements for all sections + [self _insertElementsIntoMap:map kind:kind forSections:sectionIndexes environment:environment]; + } +} + +#pragma mark - Relayout + +- (void)relayoutNodes:(id)nodes nodesSizeChanged:(NSMutableArray *)nodesSizesChanged +{ + NSParameterAssert(nodesSizesChanged); + + ASDisplayNodeAssertMainThread(); + if (!_initialReloadDataHasBeenCalled) { + return; + } + + for (ASCellNode *node in nodes) { + NSString *kind = node.collectionElement.supplementaryElementKind ?: ASDataControllerRowNodeKind; + NSIndexPath *indexPath = [_pendingMap indexPathForElement:node.collectionElement]; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + + CGSize oldSize = node.bounds.size; + [self _layoutNode:node withConstrainedSize:constrainedSize]; + if (! CGSizeEqualToSize(node.frame.size, oldSize)) { + [nodesSizesChanged addObject:node]; + } + } +} + +- (void)relayoutAllNodes +{ + ASDisplayNodeAssertMainThread(); + if (!_initialReloadDataHasBeenCalled) { + return; + } + + LOG(@"Edit Command - relayoutRows"); + dispatch_group_wait(_editingTransactionGroup, DISPATCH_TIME_FOREVER); + + // Can't relayout right away because _completedElements may not be up-to-date, + // i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedElements + dispatch_group_async(_editingTransactionGroup, _editingTransactionQueue, ^{ + [_mainSerialQueue performBlockOnMainThread:^{ + [self _relayoutAllNodes]; + }]; + }); +} + +- (void)_relayoutAllNodes +{ + ASDisplayNodeAssertMainThread(); + [_visibleMap enumerateUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + NSString *kind = element.supplementaryElementKind ?: ASDataControllerRowNodeKind; + ASSizeRange constrainedSize = [self constrainedSizeForNodeOfKind:kind atIndexPath:indexPath]; + if (ASSizeRangeHasSignificantArea(constrainedSize)) { + element.constrainedSize = constrainedSize; + + // Node may not be allocated yet (e.g node virtualization or same size optimization) + // Call context.nodeIfAllocated here to avoid immature node allocation and layout + ASCellNode *node = element.nodeIfAllocated; + if (node) { + [self _layoutNode:node withConstrainedSize:constrainedSize]; + } + } + }]; +} + ++ (NSArray *)unmeasuredElementsFromMap:(ASElementMap *)map +{ + NSMutableArray *unloadedContexts = [NSMutableArray array]; + [map enumerateUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + if (element.nodeIfAllocated.calculatedLayout == nil) { + [unloadedContexts addObject:element]; + } + }]; + return unloadedContexts; +} + +@end + +#if AS_MEASURE_AVOIDED_DATACONTROLLER_WORK + +static volatile int64_t _totalExpectedItems = 0; +static volatile int64_t _totalMeasuredNodes = 0; + +@implementation ASDataController (WorkMeasuring) + ++ (void)_didLayoutNode +{ + int64_t measured = OSAtomicIncrement64(&_totalMeasuredNodes); + int64_t expected = _totalExpectedItems; + if (measured % 20 == 0 || measured == expected) { + NSLog(@"Data controller avoided work (underestimated): %lld / %lld", measured, expected); + } +} + ++ (void)_expectToInsertNodes:(NSUInteger)count +{ + OSAtomicAdd64((int64_t)count, &_totalExpectedItems); +} + +@end +#endif diff --git a/AsyncDisplayKit/Details/ASDelegateProxy.h b/Source/Details/ASDelegateProxy.h similarity index 100% rename from AsyncDisplayKit/Details/ASDelegateProxy.h rename to Source/Details/ASDelegateProxy.h diff --git a/AsyncDisplayKit/Details/ASDelegateProxy.m b/Source/Details/ASDelegateProxy.m similarity index 100% rename from AsyncDisplayKit/Details/ASDelegateProxy.m rename to Source/Details/ASDelegateProxy.m diff --git a/AsyncDisplayKit/Details/ASEventLog.h b/Source/Details/ASEventLog.h similarity index 100% rename from AsyncDisplayKit/Details/ASEventLog.h rename to Source/Details/ASEventLog.h diff --git a/AsyncDisplayKit/Details/ASEventLog.mm b/Source/Details/ASEventLog.mm similarity index 100% rename from AsyncDisplayKit/Details/ASEventLog.mm rename to Source/Details/ASEventLog.mm diff --git a/AsyncDisplayKit/Details/ASHighlightOverlayLayer.h b/Source/Details/ASHighlightOverlayLayer.h similarity index 100% rename from AsyncDisplayKit/Details/ASHighlightOverlayLayer.h rename to Source/Details/ASHighlightOverlayLayer.h diff --git a/AsyncDisplayKit/Details/ASHighlightOverlayLayer.mm b/Source/Details/ASHighlightOverlayLayer.mm similarity index 100% rename from AsyncDisplayKit/Details/ASHighlightOverlayLayer.mm rename to Source/Details/ASHighlightOverlayLayer.mm diff --git a/AsyncDisplayKit/Details/ASImageContainerProtocolCategories.h b/Source/Details/ASImageContainerProtocolCategories.h similarity index 100% rename from AsyncDisplayKit/Details/ASImageContainerProtocolCategories.h rename to Source/Details/ASImageContainerProtocolCategories.h diff --git a/AsyncDisplayKit/Details/ASImageContainerProtocolCategories.m b/Source/Details/ASImageContainerProtocolCategories.m similarity index 100% rename from AsyncDisplayKit/Details/ASImageContainerProtocolCategories.m rename to Source/Details/ASImageContainerProtocolCategories.m diff --git a/AsyncDisplayKit/Details/ASImageProtocols.h b/Source/Details/ASImageProtocols.h similarity index 100% rename from AsyncDisplayKit/Details/ASImageProtocols.h rename to Source/Details/ASImageProtocols.h diff --git a/AsyncDisplayKit/Details/ASLayoutController.h b/Source/Details/ASLayoutController.h similarity index 92% rename from AsyncDisplayKit/Details/ASLayoutController.h rename to Source/Details/ASLayoutController.h index fd4a6d367b..bcd7818742 100644 --- a/AsyncDisplayKit/Details/ASLayoutController.h +++ b/Source/Details/ASLayoutController.h @@ -20,11 +20,6 @@ NS_ASSUME_NONNULL_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN -typedef struct { - CGFloat leadingBufferScreenfuls; - CGFloat trailingBufferScreenfuls; -} ASRangeTuningParameters; - struct ASDirectionalScreenfulBuffer { CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space. CGFloat negativeDirection; diff --git a/AsyncDisplayKit/Details/ASLayoutRangeType.h b/Source/Details/ASLayoutRangeType.h similarity index 79% rename from AsyncDisplayKit/Details/ASLayoutRangeType.h rename to Source/Details/ASLayoutRangeType.h index 068808ecdc..238ea67088 100644 --- a/AsyncDisplayKit/Details/ASLayoutRangeType.h +++ b/Source/Details/ASLayoutRangeType.h @@ -9,13 +9,25 @@ // #import +#import + +typedef struct { + CGFloat leadingBufferScreenfuls; + CGFloat trailingBufferScreenfuls; +} ASRangeTuningParameters; + +FOUNDATION_EXPORT ASRangeTuningParameters const ASRangeTuningParametersZero; + +FOUNDATION_EXPORT BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs); /** * Each mode has a complete set of tuning parameters for range types. * Depending on some conditions (including interface state and direction of the scroll view, state of rendering engine, etc), * a range controller can choose which mode it should use at a given time. */ -typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) { +typedef NS_ENUM(NSInteger, ASLayoutRangeMode) { + ASLayoutRangeModeUnspecified = -1, + /** * Minimum mode is used when a range controller should limit the amount of work it performs. * Thus, fewer views/layers are created and less data is fetched, saving system resources. @@ -44,17 +56,14 @@ typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) { * the content to be restored relatively quickly by re-decoding images (the compressed images are ~10% the size of the decoded ones, * and text is a tiny fraction of its rendered size). */ - ASLayoutRangeModeLowMemory, - ASLayoutRangeModeCount + ASLayoutRangeModeLowMemory }; -#define ASLayoutRangeModeInvalid ASLayoutRangeModeCount +static NSInteger const ASLayoutRangeModeCount = 4; typedef NS_ENUM(NSInteger, ASLayoutRangeType) { ASLayoutRangeTypeDisplay, - ASLayoutRangeTypePreload, - ASLayoutRangeTypeCount + ASLayoutRangeTypePreload }; -#define ASLayoutRangeTypeRender ASLayoutRangeTypeDisplay -#define ASLayoutRangeTypePreload ASLayoutRangeTypePreload +static NSInteger const ASLayoutRangeTypeCount = 2; diff --git a/AsyncDisplayKit/Details/ASMainSerialQueue.h b/Source/Details/ASMainSerialQueue.h similarity index 100% rename from AsyncDisplayKit/Details/ASMainSerialQueue.h rename to Source/Details/ASMainSerialQueue.h diff --git a/AsyncDisplayKit/Details/ASMainSerialQueue.mm b/Source/Details/ASMainSerialQueue.mm similarity index 100% rename from AsyncDisplayKit/Details/ASMainSerialQueue.mm rename to Source/Details/ASMainSerialQueue.mm diff --git a/AsyncDisplayKit/Details/ASMutableAttributedStringBuilder.h b/Source/Details/ASMutableAttributedStringBuilder.h similarity index 100% rename from AsyncDisplayKit/Details/ASMutableAttributedStringBuilder.h rename to Source/Details/ASMutableAttributedStringBuilder.h diff --git a/AsyncDisplayKit/Details/ASMutableAttributedStringBuilder.m b/Source/Details/ASMutableAttributedStringBuilder.m similarity index 100% rename from AsyncDisplayKit/Details/ASMutableAttributedStringBuilder.m rename to Source/Details/ASMutableAttributedStringBuilder.m diff --git a/AsyncDisplayKit/Details/ASObjectDescriptionHelpers.h b/Source/Details/ASObjectDescriptionHelpers.h similarity index 100% rename from AsyncDisplayKit/Details/ASObjectDescriptionHelpers.h rename to Source/Details/ASObjectDescriptionHelpers.h diff --git a/AsyncDisplayKit/Details/ASObjectDescriptionHelpers.m b/Source/Details/ASObjectDescriptionHelpers.m similarity index 100% rename from AsyncDisplayKit/Details/ASObjectDescriptionHelpers.m rename to Source/Details/ASObjectDescriptionHelpers.m diff --git a/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.h b/Source/Details/ASPINRemoteImageDownloader.h similarity index 100% rename from AsyncDisplayKit/Details/ASPINRemoteImageDownloader.h rename to Source/Details/ASPINRemoteImageDownloader.h diff --git a/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m b/Source/Details/ASPINRemoteImageDownloader.m similarity index 98% rename from AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m rename to Source/Details/ASPINRemoteImageDownloader.m index 65bfd17b77..7ddae236f3 100644 --- a/AsyncDisplayKit/Details/ASPINRemoteImageDownloader.m +++ b/Source/Details/ASPINRemoteImageDownloader.m @@ -166,8 +166,8 @@ static ASPINRemoteImageDownloader *sharedDownloader = nil; - (id )synchronouslyFetchedCachedImageWithURL:(NSURL *)URL; { PINRemoteImageManager *manager = [self sharedPINRemoteImageManager]; - NSString *key = [manager cacheKeyForURL:URL processorKey:nil]; - PINRemoteImageManagerResult *result = [manager synchronousImageFromCacheWithCacheKey:key options:PINRemoteImageManagerDownloadOptionsSkipDecode]; + PINRemoteImageManagerResult *result = [manager synchronousImageFromCacheWithURL:URL processorKey:nil options:PINRemoteImageManagerDownloadOptionsSkipDecode]; + #if PIN_ANIMATED_AVAILABLE if (result.alternativeRepresentation) { return result.alternativeRepresentation; diff --git a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h b/Source/Details/ASPhotosFrameworkImageRequest.h similarity index 94% rename from AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h rename to Source/Details/ASPhotosFrameworkImageRequest.h index 43b6116c6d..020992d8f7 100644 --- a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.h +++ b/Source/Details/ASPhotosFrameworkImageRequest.h @@ -61,11 +61,6 @@ extern NSString *const ASPhotosURLScheme; */ @property (nonatomic, readonly) NSURL *url; -/** - @return `YES` if `object` is an equivalent image request, `NO` otherwise. - */ -- (BOOL)isEqual:(id)object; - @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m b/Source/Details/ASPhotosFrameworkImageRequest.m similarity index 100% rename from AsyncDisplayKit/Details/ASPhotosFrameworkImageRequest.m rename to Source/Details/ASPhotosFrameworkImageRequest.m diff --git a/AsyncDisplayKit/Details/ASRangeController.h b/Source/Details/ASRangeController.h similarity index 72% rename from AsyncDisplayKit/Details/ASRangeController.h rename to Source/Details/ASRangeController.h index 4a8fae9127..73090e66b0 100644 --- a/AsyncDisplayKit/Details/ASRangeController.h +++ b/Source/Details/ASRangeController.h @@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN +@class _ASHierarchyChangeSet; @protocol ASRangeControllerDataSource; @protocol ASRangeControllerDelegate; @protocol ASLayoutController; @@ -135,9 +136,7 @@ AS_SUBCLASSING_RESTRICTED */ - (ASInterfaceState)interfaceStateForRangeController:(ASRangeController *)rangeController; -- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath; - -- (NSArray *> *)completedNodes; +- (ASElementMap *)elementMapForRangeController:(ASRangeController *)rangeController; - (NSString *)nameForRangeControllerDataSource; @@ -149,68 +148,18 @@ AS_SUBCLASSING_RESTRICTED @protocol ASRangeControllerDelegate /** - * Begin updates. + * Called before updating with given change set. * - * @param rangeController Sender. + * @param changeSet The change set that includes all updates */ -- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController; +- (void)rangeController:(ASRangeController *)rangeController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; /** - * End updates. + * Called after updating with given change set. * - * @param rangeController Sender. - * @param animated NO if all animations are disabled. YES otherwise. - * @param completion Completion block. + * @param changeSet The change set that includes all updates */ -- (void)rangeController:(ASRangeController * )rangeController didEndUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion; - -/** - * Called for nodes insertion. - * - * @param rangeController Sender. - * - * @param nodes Inserted nodes. - * - * @param indexPaths Index path of inserted nodes. - * - * @param animationOptions Animation options. See ASDataControllerAnimationOptions. - */ -- (void)rangeController:(ASRangeController *)rangeController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - * Called for nodes deletion. - * - * @param rangeController Sender. - * - * @param nodes Deleted nodes. - * - * @param indexPaths Index path of deleted nodes. - * - * @param animationOptions Animation options. See ASDataControllerAnimationOptions. - */ -- (void)rangeController:(ASRangeController *)rangeController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - * Called for section insertion. - * - * @param rangeController Sender. - * - * @param indexSet Index set of inserted sections. - * - * @param animationOptions Animation options. See ASDataControllerAnimationOptions. - */ -- (void)rangeController:(ASRangeController *)rangeController didInsertSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; - -/** - * Called for section deletion. - * - * @param rangeController Sender. - * - * @param indexSet Index set of deleted sections. - * - * @param animationOptions Animation options. See ASDataControllerAnimationOptions. - */ -- (void)rangeController:(ASRangeController *)rangeController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions; +- (void)rangeController:(ASRangeController *)rangeController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet; @end @@ -222,8 +171,8 @@ AS_SUBCLASSING_RESTRICTED * * Logic for the automatic range mode: * 1. If there are no visible node paths available nothing is to be done and no range update will happen - * 2. The initial range update if the range controller is visible always will be ASLayoutRangeModeCount - * (ASLayoutRangeModeMinimum) as it's the initial fetch + * 2. The initial range update if the range controller is visible always will be + * ASLayoutRangeModeMinimum as it's the initial fetch * 3. The range mode set explicitly via updateCurrentRangeWithMode: will last at least one range update. After that it the range controller will use the explicit set range mode until it becomes visible and a new range update was triggered or a new range mode via updateCurrentRangeWithMode: is set @@ -233,6 +182,22 @@ AS_SUBCLASSING_RESTRICTED @end +@interface ASRangeController (DebugInternal) + ++ (void)layoutDebugOverlayIfNeeded; + +- (void)addRangeControllerToRangeDebugOverlay; + +- (void)updateRangeController:(ASRangeController *)controller + withScrollableDirections:(ASScrollDirection)scrollableDirections + scrollDirection:(ASScrollDirection)direction + rangeMode:(ASLayoutRangeMode)mode + displayTuningParameters:(ASRangeTuningParameters)displayTuningParameters + preloadTuningParameters:(ASRangeTuningParameters)preloadTuningParameters + interfaceState:(ASInterfaceState)interfaceState; + +@end + NS_ASSUME_NONNULL_END #endif diff --git a/AsyncDisplayKit/Details/ASRangeController.mm b/Source/Details/ASRangeController.mm similarity index 82% rename from AsyncDisplayKit/Details/ASRangeController.mm rename to Source/Details/ASRangeController.mm index e0a3a7d77b..d608696578 100644 --- a/AsyncDisplayKit/Details/ASRangeController.mm +++ b/Source/Details/ASRangeController.mm @@ -11,10 +11,13 @@ #ifndef MINIMAL_ASDK #import +#import #import #import +#import #import #import // Required for interfaceState and hierarchyState setter methods. +#import #import #import #import @@ -68,7 +71,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; } _rangeIsValid = YES; - _currentRangeMode = ASLayoutRangeModeInvalid; + _currentRangeMode = ASLayoutRangeModeUnspecified; _preserveCurrentRangeMode = NO; _previousScrollDirection = ASScrollDirectionDown | ASScrollDirectionRight; @@ -79,7 +82,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; #endif - if ([ASRangeController shouldShowRangeDebugOverlay]) { + if (ASDisplayNode.shouldShowRangeDebugOverlay) { [self addRangeControllerToRangeDebugOverlay]; } @@ -101,7 +104,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; + (BOOL)isFirstRangeUpdateForRangeMode:(ASLayoutRangeMode)rangeMode { - return (rangeMode == ASLayoutRangeModeInvalid); + return (rangeMode == ASLayoutRangeModeUnspecified); } + (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState @@ -207,9 +210,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; _updateCountThisFrame += 1; #endif - // allNodes is a 2D array: it contains arrays for each section, each containing nodes. - NSArray *allNodes = [_dataSource completedNodes]; - NSUInteger numberOfSections = [allNodes count]; + ASElementMap *map = [_dataSource elementMapForRangeController:self]; // TODO: Consider if we need to use this codepath, or can rely on something more similar to the data & display ranges // Example: ... = [_layoutController indexPathsForScrolling:scrollDirection rangeType:ASLayoutRangeTypeVisible]; @@ -238,10 +239,6 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; [_layoutController setVisibleNodeIndexPaths:visibleNodePaths]; } - NSArray *currentSectionNodes = nil; - NSInteger currentSectionIndex = -1; // Set to -1 so we don't match any indexPath.section on the first iteration. - NSUInteger numberOfNodesInSection = 0; - NSSet *visibleIndexPaths = [NSSet setWithArray:visibleNodePaths]; NSSet *displayIndexPaths = nil; NSSet *preloadIndexPaths = nil; @@ -300,7 +297,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; _preserveCurrentRangeMode = NO; if (!_rangeIsValid) { - [allIndexPaths addObjectsFromArray:ASIndexPathsForTwoDimensionalArray(allNodes)]; + [allIndexPaths addObjectsFromArray:map.itemIndexPaths]; } #if ASRangeControllerLoggingEnabled @@ -344,40 +341,26 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; } } } - - NSInteger section = indexPath.section; - NSInteger row = indexPath.row; - - if (section >= 0 && row >= 0 && section < numberOfSections) { - if (section != currentSectionIndex) { - // Often we'll be dealing with indexPaths in the same section, but the set isn't sorted and we may even bounce - // between the same ones. Still, this saves dozens of method calls to access the inner array and count. - currentSectionNodes = allNodes[section]; - numberOfNodesInSection = [currentSectionNodes count]; - currentSectionIndex = section; + + ASCellNode *node = [map elementForItemAtIndexPath:indexPath].nodeIfAllocated; + if (node != nil) { + ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); + if (ASInterfaceStateIncludesVisible(interfaceState)) { + [newVisibleNodes addObject:node]; } - - if (row < numberOfNodesInSection) { - ASDisplayNode *node = currentSectionNodes[row]; - - ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset."); - if (ASInterfaceStateIncludesVisible(interfaceState)) { - [newVisibleNodes addObject:node]; - } - // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. - if (node.interfaceState != interfaceState) { + // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. + if (node.interfaceState != interfaceState) { #if ASRangeControllerLoggingEnabled - [modifiedIndexPaths addObject:indexPath]; + [modifiedIndexPaths addObject:indexPath]; #endif - - BOOL nodeShouldScheduleDisplay = [node shouldScheduleDisplayWithNewInterfaceState:interfaceState]; - [node recursivelySetInterfaceState:interfaceState]; - - if (nodeShouldScheduleDisplay) { - [self registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:selfInterfaceState]; - if (_didRegisterForNodeDisplayNotifications) { - _pendingDisplayNodesTimestamp = CACurrentMediaTime(); - } + + BOOL nodeShouldScheduleDisplay = [node shouldScheduleDisplayWithNewInterfaceState:interfaceState]; + [node recursivelySetInterfaceState:interfaceState]; + + if (nodeShouldScheduleDisplay) { + [self registerForNodeDisplayNotificationsForInterfaceStateIfNeeded:selfInterfaceState]; + if (_didRegisterForNodeDisplayNotifications) { + _pendingDisplayNodesTimestamp = CACurrentMediaTime(); } } } @@ -387,7 +370,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; [self _setVisibleNodes:newVisibleNodes]; // TODO: This code is for debugging only, but would be great to clean up with a delegate method implementation. - if ([ASRangeController shouldShowRangeDebugOverlay]) { + if (ASDisplayNode.shouldShowRangeDebugOverlay) { ASScrollDirection scrollableDirections = ASScrollDirectionUp | ASScrollDirectionDown; if ([_dataSource isKindOfClass:NSClassFromString(@"ASCollectionView")]) { #pragma clang diagnostic push @@ -484,51 +467,20 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; #pragma mark - ASDataControllerDelegete -- (void)dataControllerBeginUpdates:(ASDataController *)dataController +- (void)dataController:(ASDataController *)dataController willUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); - [_delegate didBeginUpdatesInRangeController:self]; + if (changeSet.includesReloadData) { + [self _setVisibleNodes:nil]; + } + [_delegate rangeController:self willUpdateWithChangeSet:changeSet]; } -- (void)dataControllerWillDeleteAllData:(ASDataController *)dataController -{ - [self _setVisibleNodes:nil]; -} - -- (void)dataController:(ASDataController *)dataController endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion -{ - ASDisplayNodeAssertMainThread(); - [_delegate rangeController:self didEndUpdatesAnimated:animated completion:completion]; -} - -- (void)dataController:(ASDataController *)dataController didInsertNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssert(nodes.count == indexPaths.count, @"Invalid index path"); - ASDisplayNodeAssertMainThread(); - _rangeIsValid = NO; - [_delegate rangeController:self didInsertNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; -} - -- (void)dataController:(ASDataController *)dataController didDeleteNodes:(NSArray *)nodes atIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions +- (void)dataController:(ASDataController *)dataController didUpdateWithChangeSet:(_ASHierarchyChangeSet *)changeSet { ASDisplayNodeAssertMainThread(); _rangeIsValid = NO; - [_delegate rangeController:self didDeleteNodes:nodes atIndexPaths:indexPaths withAnimationOptions:animationOptions]; -} - -- (void)dataController:(ASDataController *)dataController didInsertSections:(NSArray *)sections atIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssert(sections.count == indexSet.count, @"Invalid sections"); - ASDisplayNodeAssertMainThread(); - _rangeIsValid = NO; - [_delegate rangeController:self didInsertSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; -} - -- (void)dataController:(ASDataController *)dataController didDeleteSectionsAtIndexSet:(NSIndexSet *)indexSet withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions -{ - ASDisplayNodeAssertMainThread(); - _rangeIsValid = NO; - [_delegate rangeController:self didDeleteSectionsAtIndexSet:indexSet withAnimationOptions:animationOptions]; + [_delegate rangeController:self didUpdateWithChangeSet:changeSet]; } #pragma mark - Memory Management @@ -536,24 +488,22 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive; // Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState. - (void)clearContents { - for (NSArray *section in [_dataSource completedNodes]) { - for (ASDisplayNode *node in section) { - if (ASInterfaceStateIncludesDisplay(node.interfaceState)) { - [node exitInterfaceState:ASInterfaceStateDisplay]; - } + [[_dataSource elementMapForRangeController:self] enumerateUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + ASCellNode *node = element.nodeIfAllocated; + if (ASInterfaceStateIncludesDisplay(node.interfaceState)) { + [node exitInterfaceState:ASInterfaceStateDisplay]; } - } + }]; } - (void)clearPreloadedData { - for (NSArray *section in [_dataSource completedNodes]) { - for (ASDisplayNode *node in section) { - if (ASInterfaceStateIncludesPreload(node.interfaceState)) { - [node exitInterfaceState:ASInterfaceStatePreload]; - } + [[_dataSource elementMapForRangeController:self] enumerateUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + ASCellNode *node = element.nodeIfAllocated; + if (ASInterfaceStateIncludesPreload(node.interfaceState)) { + [node exitInterfaceState:ASInterfaceStatePreload]; } - } + }]; } #pragma mark - Class Methods (Application Notification Handlers) @@ -656,7 +606,7 @@ static ASLayoutRangeMode __rangeModeForMemoryWarnings = ASLayoutRangeModeLowMemo { NSMutableString *description = [NSMutableString stringWithFormat:@"%@ %@", [super description], @" allPreviousIndexPaths:\n"]; for (NSIndexPath *indexPath in indexPaths) { - ASDisplayNode *node = [_dataSource rangeController:self nodeAtIndexPath:indexPath]; + ASDisplayNode *node = [[_dataSource elementMapForRangeController:self] elementForItemAtIndexPath:indexPath].nodeIfAllocated; ASInterfaceState interfaceState = node.interfaceState; BOOL inVisible = ASInterfaceStateIncludesVisible(interfaceState); BOOL inDisplay = ASInterfaceStateIncludesDisplay(interfaceState); diff --git a/AsyncDisplayKit/Details/ASRangeControllerUpdateRangeProtocol+Beta.h b/Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h similarity index 100% rename from AsyncDisplayKit/Details/ASRangeControllerUpdateRangeProtocol+Beta.h rename to Source/Details/ASRangeControllerUpdateRangeProtocol+Beta.h diff --git a/AsyncDisplayKit/Details/ASScrollDirection.h b/Source/Details/ASScrollDirection.h similarity index 100% rename from AsyncDisplayKit/Details/ASScrollDirection.h rename to Source/Details/ASScrollDirection.h diff --git a/AsyncDisplayKit/Details/ASScrollDirection.m b/Source/Details/ASScrollDirection.m similarity index 100% rename from AsyncDisplayKit/Details/ASScrollDirection.m rename to Source/Details/ASScrollDirection.m diff --git a/AsyncDisplayKit/Details/ASSectionContext.h b/Source/Details/ASSectionContext.h similarity index 100% rename from AsyncDisplayKit/Details/ASSectionContext.h rename to Source/Details/ASSectionContext.h diff --git a/AsyncDisplayKit/Details/ASTableLayoutController.h b/Source/Details/ASTableLayoutController.h similarity index 100% rename from AsyncDisplayKit/Details/ASTableLayoutController.h rename to Source/Details/ASTableLayoutController.h diff --git a/AsyncDisplayKit/Details/ASTableLayoutController.m b/Source/Details/ASTableLayoutController.m similarity index 100% rename from AsyncDisplayKit/Details/ASTableLayoutController.m rename to Source/Details/ASTableLayoutController.m diff --git a/AsyncDisplayKit/Details/ASThread.h b/Source/Details/ASThread.h similarity index 100% rename from AsyncDisplayKit/Details/ASThread.h rename to Source/Details/ASThread.h diff --git a/AsyncDisplayKit/Details/ASTraceEvent.h b/Source/Details/ASTraceEvent.h similarity index 100% rename from AsyncDisplayKit/Details/ASTraceEvent.h rename to Source/Details/ASTraceEvent.h diff --git a/AsyncDisplayKit/Details/ASTraceEvent.m b/Source/Details/ASTraceEvent.m similarity index 100% rename from AsyncDisplayKit/Details/ASTraceEvent.m rename to Source/Details/ASTraceEvent.m diff --git a/AsyncDisplayKit/Details/ASTraitCollection.h b/Source/Details/ASTraitCollection.h similarity index 94% rename from AsyncDisplayKit/Details/ASTraitCollection.h rename to Source/Details/ASTraitCollection.h index df12579715..51215855d8 100644 --- a/AsyncDisplayKit/Details/ASTraitCollection.h +++ b/Source/Details/ASTraitCollection.h @@ -92,7 +92,7 @@ ASDISPLAYNODE_EXTERN_C_END * Deprecated and should be replaced by the methods from above */ - (ASEnvironmentTraitCollection)environmentTraitCollection; -- (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traitCollection;; +- (void)setEnvironmentTraitCollection:(ASEnvironmentTraitCollection)traitCollection; @end @@ -122,14 +122,12 @@ ASDISPLAYNODE_EXTERN_C_END \ ASPrimitiveTraitCollection currentTraits = self.primitiveTraitCollection;\ if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(currentTraits, oldTraits) == NO) {\ - /* Must dispatch to main for self.view && [self.view.dataController completedNodes]*/\ + /* Must dispatch to main for self.view && [self.view.dataController visibleMap]*/\ ASPerformBlockOnMainThread(^{\ - NSArray *> *completedNodes = [self.view.dataController completedNodes];\ - for (NSArray *sectionArray in completedNodes) {\ - for (ASCellNode *cellNode in sectionArray) {\ - ASTraitCollectionPropagateDown(cellNode, currentTraits);\ - }\ - }\ + ASElementMap *map = self.view.dataController.visibleMap; \ + [map enumerateUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { \ + ASTraitCollectionPropagateDown(element.nodeIfAllocated, currentTraits); \ + }]; \ });\ }\ }\ diff --git a/AsyncDisplayKit/Details/ASTraitCollection.m b/Source/Details/ASTraitCollection.m similarity index 100% rename from AsyncDisplayKit/Details/ASTraitCollection.m rename to Source/Details/ASTraitCollection.m diff --git a/AsyncDisplayKit/Details/ASWeakProxy.h b/Source/Details/ASWeakProxy.h similarity index 100% rename from AsyncDisplayKit/Details/ASWeakProxy.h rename to Source/Details/ASWeakProxy.h diff --git a/AsyncDisplayKit/Details/ASWeakProxy.m b/Source/Details/ASWeakProxy.m similarity index 100% rename from AsyncDisplayKit/Details/ASWeakProxy.m rename to Source/Details/ASWeakProxy.m diff --git a/AsyncDisplayKit/Details/ASWeakSet.h b/Source/Details/ASWeakSet.h similarity index 100% rename from AsyncDisplayKit/Details/ASWeakSet.h rename to Source/Details/ASWeakSet.h diff --git a/AsyncDisplayKit/Details/ASWeakSet.m b/Source/Details/ASWeakSet.m similarity index 100% rename from AsyncDisplayKit/Details/ASWeakSet.m rename to Source/Details/ASWeakSet.m diff --git a/AsyncDisplayKit/Details/CoreGraphics+ASConvenience.h b/Source/Details/CoreGraphics+ASConvenience.h similarity index 100% rename from AsyncDisplayKit/Details/CoreGraphics+ASConvenience.h rename to Source/Details/CoreGraphics+ASConvenience.h diff --git a/AsyncDisplayKit/Details/CoreGraphics+ASConvenience.m b/Source/Details/CoreGraphics+ASConvenience.m similarity index 100% rename from AsyncDisplayKit/Details/CoreGraphics+ASConvenience.m rename to Source/Details/CoreGraphics+ASConvenience.m diff --git a/AsyncDisplayKit/Details/NSArray+Diffing.h b/Source/Details/NSArray+Diffing.h similarity index 100% rename from AsyncDisplayKit/Details/NSArray+Diffing.h rename to Source/Details/NSArray+Diffing.h diff --git a/AsyncDisplayKit/Details/NSArray+Diffing.m b/Source/Details/NSArray+Diffing.m similarity index 86% rename from AsyncDisplayKit/Details/NSArray+Diffing.m rename to Source/Details/NSArray+Diffing.m index 44abf7ed71..b1b3b64e98 100644 --- a/AsyncDisplayKit/Details/NSArray+Diffing.m +++ b/Source/Details/NSArray+Diffing.m @@ -60,18 +60,19 @@ NSInteger arrayCount = array.count; // Allocate the diff map in the heap so we don't blow the stack for large arrays. - NSInteger (*lengths)[arrayCount+1] = NULL; - size_t lengthsSize = ((selfCount+1) * sizeof(*lengths)); - // Would rather use initWithCapacity: to skip the zeroing, but TECHNICALLY - // `mutableBytes` is only guaranteed to be non-NULL if the data object has a non-zero length. - NS_VALID_UNTIL_END_OF_SCOPE NSMutableData *lengthsData = [[NSMutableData alloc] initWithLength:lengthsSize]; - lengths = lengthsData.mutableBytes; + NSInteger **lengths = NULL; + lengths = (NSInteger **)malloc(sizeof(NSInteger*) * (selfCount+1)); if (lengths == NULL) { - ASDisplayNodeFailAssert(@"Failed to allocate memory for diffing with size %tu", lengthsSize); + ASDisplayNodeFailAssert(@"Failed to allocate memory for diffing"); return nil; } for (NSInteger i = 0; i <= selfCount; i++) { + lengths[i] = (NSInteger *)malloc(sizeof(NSInteger) * (arrayCount+1)); + if (lengths[i] == NULL) { + ASDisplayNodeFailAssert(@"Failed to allocate memory for diffing"); + return nil; + } id selfObj = i > 0 ? self[i-1] : nil; for (NSInteger j = 0; j <= arrayCount; j++) { if (i == 0 || j == 0) { @@ -96,7 +97,11 @@ j--; } } - + + for (NSInteger i = 0; i <= selfCount; i++) { + free(lengths[i]); + } + free(lengths); return common; } diff --git a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h b/Source/Details/NSIndexSet+ASHelpers.h similarity index 91% rename from AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h rename to Source/Details/NSIndexSet+ASHelpers.h index d6d3238831..a816b102f4 100644 --- a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.h +++ b/Source/Details/NSIndexSet+ASHelpers.h @@ -25,4 +25,6 @@ /// Returns all the section indexes contained in the index paths array. + (NSIndexSet *)as_sectionsFromIndexPaths:(NSArray *)indexPaths; +- (NSArray *)as_filterIndexPathsBySection:(id)indexPaths; + @end diff --git a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m b/Source/Details/NSIndexSet+ASHelpers.m similarity index 89% rename from AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m rename to Source/Details/NSIndexSet+ASHelpers.m index ea7d6d8a38..d0e3f34b83 100644 --- a/AsyncDisplayKit/Details/NSIndexSet+ASHelpers.m +++ b/Source/Details/NSIndexSet+ASHelpers.m @@ -92,4 +92,15 @@ return result; } +- (NSArray *)as_filterIndexPathsBySection:(id)indexPaths +{ + NSMutableArray *result = [NSMutableArray array]; + for (NSIndexPath *indexPath in indexPaths) { + if ([self containsIndex:indexPath.section]) { + [result addObject:indexPath]; + } + } + return result; +} + @end diff --git a/AsyncDisplayKit/Details/NSMutableAttributedString+TextKitAdditions.h b/Source/Details/NSMutableAttributedString+TextKitAdditions.h similarity index 100% rename from AsyncDisplayKit/Details/NSMutableAttributedString+TextKitAdditions.h rename to Source/Details/NSMutableAttributedString+TextKitAdditions.h diff --git a/AsyncDisplayKit/Details/NSMutableAttributedString+TextKitAdditions.m b/Source/Details/NSMutableAttributedString+TextKitAdditions.m similarity index 100% rename from AsyncDisplayKit/Details/NSMutableAttributedString+TextKitAdditions.m rename to Source/Details/NSMutableAttributedString+TextKitAdditions.m diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.h b/Source/Details/Transactions/_ASAsyncTransaction.h similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.h rename to Source/Details/Transactions/_ASAsyncTransaction.h diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm b/Source/Details/Transactions/_ASAsyncTransaction.mm similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransaction.mm rename to Source/Details/Transactions/_ASAsyncTransaction.mm diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer+Private.h b/Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer+Private.h rename to Source/Details/Transactions/_ASAsyncTransactionContainer+Private.h diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h b/Source/Details/Transactions/_ASAsyncTransactionContainer.h similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h rename to Source/Details/Transactions/_ASAsyncTransactionContainer.h diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.m b/Source/Details/Transactions/_ASAsyncTransactionContainer.m similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.m rename to Source/Details/Transactions/_ASAsyncTransactionContainer.m diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionGroup.h b/Source/Details/Transactions/_ASAsyncTransactionGroup.h similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionGroup.h rename to Source/Details/Transactions/_ASAsyncTransactionGroup.h diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionGroup.m b/Source/Details/Transactions/_ASAsyncTransactionGroup.m similarity index 100% rename from AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionGroup.m rename to Source/Details/Transactions/_ASAsyncTransactionGroup.m diff --git a/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h b/Source/Details/UICollectionViewLayout+ASConvenience.h similarity index 100% rename from AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.h rename to Source/Details/UICollectionViewLayout+ASConvenience.h diff --git a/AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m b/Source/Details/UICollectionViewLayout+ASConvenience.m similarity index 100% rename from AsyncDisplayKit/Details/UICollectionViewLayout+ASConvenience.m rename to Source/Details/UICollectionViewLayout+ASConvenience.m diff --git a/AsyncDisplayKit/Details/UIView+ASConvenience.h b/Source/Details/UIView+ASConvenience.h similarity index 100% rename from AsyncDisplayKit/Details/UIView+ASConvenience.h rename to Source/Details/UIView+ASConvenience.h diff --git a/AsyncDisplayKit/Details/_ASCollectionViewCell.h b/Source/Details/_ASCollectionViewCell.h similarity index 100% rename from AsyncDisplayKit/Details/_ASCollectionViewCell.h rename to Source/Details/_ASCollectionViewCell.h diff --git a/AsyncDisplayKit/Details/_ASCollectionViewCell.m b/Source/Details/_ASCollectionViewCell.m similarity index 95% rename from AsyncDisplayKit/Details/_ASCollectionViewCell.m rename to Source/Details/_ASCollectionViewCell.m index 103f6a3353..d3bea03265 100644 --- a/AsyncDisplayKit/Details/_ASCollectionViewCell.m +++ b/Source/Details/_ASCollectionViewCell.m @@ -18,8 +18,7 @@ ASDisplayNodeAssertMainThread(); node.layoutAttributes = _layoutAttributes; _node = node; - self.backgroundColor = node.backgroundColor; - self.clipsToBounds = node.clipsToBounds; + [node __setSelectedFromUIKit:self.selected]; [node __setHighlightedFromUIKit:self.highlighted]; } diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.h b/Source/Details/_ASDisplayLayer.h similarity index 100% rename from AsyncDisplayKit/Details/_ASDisplayLayer.h rename to Source/Details/_ASDisplayLayer.h diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.mm b/Source/Details/_ASDisplayLayer.mm similarity index 100% rename from AsyncDisplayKit/Details/_ASDisplayLayer.mm rename to Source/Details/_ASDisplayLayer.mm diff --git a/AsyncDisplayKit/Details/_ASDisplayView.h b/Source/Details/_ASDisplayView.h similarity index 100% rename from AsyncDisplayKit/Details/_ASDisplayView.h rename to Source/Details/_ASDisplayView.h diff --git a/AsyncDisplayKit/Details/_ASDisplayView.mm b/Source/Details/_ASDisplayView.mm similarity index 100% rename from AsyncDisplayKit/Details/_ASDisplayView.mm rename to Source/Details/_ASDisplayView.mm diff --git a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.h b/Source/Details/_ASDisplayViewAccessiblity.h similarity index 100% rename from AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.h rename to Source/Details/_ASDisplayViewAccessiblity.h diff --git a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm b/Source/Details/_ASDisplayViewAccessiblity.mm similarity index 99% rename from AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm rename to Source/Details/_ASDisplayViewAccessiblity.mm index aa9488b739..b6d174a5de 100644 --- a/AsyncDisplayKit/Details/_ASDisplayViewAccessiblity.mm +++ b/Source/Details/_ASDisplayViewAccessiblity.mm @@ -14,6 +14,7 @@ #import #import #import +#import #pragma mark - UIAccessibilityElement diff --git a/AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.h b/Source/IGListAdapter+AsyncDisplayKit.h similarity index 100% rename from AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.h rename to Source/IGListAdapter+AsyncDisplayKit.h diff --git a/AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.m b/Source/IGListAdapter+AsyncDisplayKit.m similarity index 100% rename from AsyncDisplayKit/IGListAdapter+AsyncDisplayKit.m rename to Source/IGListAdapter+AsyncDisplayKit.m diff --git a/AsyncDisplayKit/Info.plist b/Source/Info.plist similarity index 100% rename from AsyncDisplayKit/Info.plist rename to Source/Info.plist diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h b/Source/Layout/ASAbsoluteLayoutElement.h similarity index 100% rename from AsyncDisplayKit/Layout/ASAbsoluteLayoutElement.h rename to Source/Layout/ASAbsoluteLayoutElement.h diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h b/Source/Layout/ASAbsoluteLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.h rename to Source/Layout/ASAbsoluteLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm b/Source/Layout/ASAbsoluteLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASAbsoluteLayoutSpec.mm rename to Source/Layout/ASAbsoluteLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h b/Source/Layout/ASAsciiArtBoxCreator.h similarity index 100% rename from AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.h rename to Source/Layout/ASAsciiArtBoxCreator.h diff --git a/AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m b/Source/Layout/ASAsciiArtBoxCreator.m similarity index 100% rename from AsyncDisplayKit/Layout/ASAsciiArtBoxCreator.m rename to Source/Layout/ASAsciiArtBoxCreator.m diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h b/Source/Layout/ASBackgroundLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.h rename to Source/Layout/ASBackgroundLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/Source/Layout/ASBackgroundLayoutSpec.mm similarity index 88% rename from AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm rename to Source/Layout/ASBackgroundLayoutSpec.mm index 657d44446d..a3fb85e078 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/Source/Layout/ASBackgroundLayoutSpec.mm @@ -33,9 +33,7 @@ static NSUInteger const kBackgroundChildIndex = 1; if (!(self = [super init])) { return nil; } - - ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); - [self setChild:child atIndex:kForegroundChildIndex]; + self.child = child; self.background = background; return self; } @@ -49,7 +47,7 @@ static NSUInteger const kBackgroundChildIndex = 1; restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize { - ASLayout *contentsLayout = [[super childAtIndex:kForegroundChildIndex] layoutThatFits:constrainedSize parentSize:parentSize]; + ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2]; if (self.background) { @@ -67,6 +65,17 @@ static NSUInteger const kBackgroundChildIndex = 1; #pragma mark - Background +- (void)setChild:(id)child +{ + ASDisplayNodeAssertNotNil(child, @"Child cannot be nil"); + [super setChild:child atIndex:kForegroundChildIndex]; +} + +- (id)child +{ + return [super childAtIndex:kForegroundChildIndex]; +} + - (void)setBackground:(id)background { ASDisplayNodeAssertNotNil(background, @"Background cannot be nil"); diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.h b/Source/Layout/ASCenterLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASCenterLayoutSpec.h rename to Source/Layout/ASCenterLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm b/Source/Layout/ASCenterLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASCenterLayoutSpec.mm rename to Source/Layout/ASCenterLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASDimension.h b/Source/Layout/ASDimension.h similarity index 94% rename from AsyncDisplayKit/Layout/ASDimension.h rename to Source/Layout/ASDimension.h index 10237f9db6..cb14eef003 100644 --- a/AsyncDisplayKit/Layout/ASDimension.h +++ b/Source/Layout/ASDimension.h @@ -39,6 +39,21 @@ ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForSize(CGSize si return (ASPointsValidForSize(size.width) && ASPointsValidForSize(size.height)); } +ASDISPLAYNODE_INLINE BOOL ASIsCGPositionPointsValidForLayout(CGFloat points) +{ + return ((isnormal(points) || points == 0.0) && points < (CGFLOAT_MAX / 2.0)); +} + +ASDISPLAYNODE_INLINE BOOL ASIsCGPositionValidForLayout(CGPoint point) +{ + return (ASIsCGPositionPointsValidForLayout(point.x) && ASIsCGPositionPointsValidForLayout(point.y)); +} + +ASDISPLAYNODE_INLINE BOOL ASIsCGRectValidForLayout(CGRect rect) +{ + return (ASIsCGPositionValidForLayout(rect.origin) && ASIsCGSizeValidForLayout(rect.size)); +} + #pragma mark - ASDimension /** @@ -200,6 +215,8 @@ typedef struct { ASDimension right; } ASEdgeInsets; +extern ASEdgeInsets const ASEdgeInsetsZero; + #pragma mark - ASSizeRange /** diff --git a/AsyncDisplayKit/Layout/ASDimension.mm b/Source/Layout/ASDimension.mm similarity index 97% rename from AsyncDisplayKit/Layout/ASDimension.mm rename to Source/Layout/ASDimension.mm index d11ebe78ca..6db911d11b 100644 --- a/AsyncDisplayKit/Layout/ASDimension.mm +++ b/Source/Layout/ASDimension.mm @@ -64,6 +64,9 @@ NSString *NSStringFromASDimension(ASDimension dimension) ASLayoutSize const ASLayoutSizeAuto = {ASDimensionAuto, ASDimensionAuto}; +#pragma mark - ASEdgeInsets + +ASEdgeInsets const ASEdgeInsetsZero = {}; #pragma mark - ASSizeRange diff --git a/AsyncDisplayKit/Layout/ASDimensionDeprecated.h b/Source/Layout/ASDimensionDeprecated.h similarity index 100% rename from AsyncDisplayKit/Layout/ASDimensionDeprecated.h rename to Source/Layout/ASDimensionDeprecated.h diff --git a/AsyncDisplayKit/Layout/ASDimensionDeprecated.mm b/Source/Layout/ASDimensionDeprecated.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASDimensionDeprecated.mm rename to Source/Layout/ASDimensionDeprecated.mm diff --git a/AsyncDisplayKit/Layout/ASDimensionInternal.h b/Source/Layout/ASDimensionInternal.h similarity index 100% rename from AsyncDisplayKit/Layout/ASDimensionInternal.h rename to Source/Layout/ASDimensionInternal.h diff --git a/AsyncDisplayKit/Layout/ASDimensionInternal.mm b/Source/Layout/ASDimensionInternal.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASDimensionInternal.mm rename to Source/Layout/ASDimensionInternal.mm diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.h b/Source/Layout/ASInsetLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASInsetLayoutSpec.h rename to Source/Layout/ASInsetLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/Source/Layout/ASInsetLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm rename to Source/Layout/ASInsetLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/Source/Layout/ASLayout.h similarity index 97% rename from AsyncDisplayKit/Layout/ASLayout.h rename to Source/Layout/ASLayout.h index b41f1259db..b592ddb13b 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/Source/Layout/ASLayout.h @@ -70,6 +70,12 @@ ASDISPLAYNODE_EXTERN_C_END */ @property (nonatomic, copy, readonly) NSArray *sublayouts; +/** + * The frame for the given element, or CGRectNull if + * the element is not a direct descendent of this layout. + */ +- (CGRect)frameForElement:(id)layoutElement; + /** * @abstract Returns a valid frame for the current layout computed with the size and position. * @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite. diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/Source/Layout/ASLayout.mm similarity index 95% rename from AsyncDisplayKit/Layout/ASLayout.mm rename to Source/Layout/ASLayout.mm index f01f03592f..aaa569ee4a 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/Source/Layout/ASLayout.mm @@ -18,6 +18,7 @@ #import #import +#import CGPoint const CGPointNull = {NAN, NAN}; @@ -60,6 +61,8 @@ static inline NSString * descriptionIndents(NSUInteger indents) */ @property (nonatomic, strong) NSMutableArray> *sublayoutLayoutElements; +@property (nonatomic, strong, readonly) ASRectTable, id> *elementToRectTable; + @end @implementation ASLayout @@ -101,6 +104,12 @@ static inline NSString * descriptionIndents(NSUInteger indents) } _sublayouts = sublayouts != nil ? [sublayouts copy] : @[]; + + _elementToRectTable = [ASRectTable rectTableForWeakObjectPointers]; + for (ASLayout *layout in sublayouts) { + [_elementToRectTable setRect:layout.frame forKey:layout.layoutElement]; + } + _flattened = NO; _retainSublayoutLayoutElements = NO; } @@ -221,6 +230,11 @@ static inline NSString * descriptionIndents(NSUInteger indents) return _layoutElementType; } +- (CGRect)frameForElement:(id)layoutElement +{ + return [_elementToRectTable rectForKey:layoutElement]; +} + - (CGRect)frame { CGRect subnodeFrame = CGRectZero; diff --git a/AsyncDisplayKit/Layout/ASLayoutElement.h b/Source/Layout/ASLayoutElement.h similarity index 99% rename from AsyncDisplayKit/Layout/ASLayoutElement.h rename to Source/Layout/ASLayoutElement.h index 0e982a7b38..3af69103f8 100644 --- a/AsyncDisplayKit/Layout/ASLayoutElement.h +++ b/Source/Layout/ASLayoutElement.h @@ -77,7 +77,7 @@ ASDISPLAYNODE_EXTERN_C_END /** * @abstract Returns type of layoutElement */ -@property (nonatomic, assign, readonly) ASLayoutElementType layoutElementType;; +@property (nonatomic, assign, readonly) ASLayoutElementType layoutElementType; /** * @abstract A size constraint that should apply to this ASLayoutElement. diff --git a/AsyncDisplayKit/Layout/ASLayoutElement.mm b/Source/Layout/ASLayoutElement.mm similarity index 99% rename from AsyncDisplayKit/Layout/ASLayoutElement.mm rename to Source/Layout/ASLayoutElement.mm index 7c055c94c9..de88730dd1 100644 --- a/AsyncDisplayKit/Layout/ASLayoutElement.mm +++ b/Source/Layout/ASLayoutElement.mm @@ -20,7 +20,7 @@ #import #if YOGA - #import + #import YOGA_HEADER_PATH #endif extern void ASLayoutElementPerformBlockOnEveryElement(id element, void(^block)(id element)) diff --git a/AsyncDisplayKit/Layout/ASLayoutElementExtensibility.h b/Source/Layout/ASLayoutElementExtensibility.h similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutElementExtensibility.h rename to Source/Layout/ASLayoutElementExtensibility.h diff --git a/AsyncDisplayKit/Layout/ASLayoutElementPrivate.h b/Source/Layout/ASLayoutElementPrivate.h similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutElementPrivate.h rename to Source/Layout/ASLayoutElementPrivate.h diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h b/Source/Layout/ASLayoutSpec+Subclasses.h similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.h rename to Source/Layout/ASLayoutSpec+Subclasses.h diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm b/Source/Layout/ASLayoutSpec+Subclasses.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutSpec+Subclasses.mm rename to Source/Layout/ASLayoutSpec+Subclasses.mm diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/Source/Layout/ASLayoutSpec.h similarity index 94% rename from AsyncDisplayKit/Layout/ASLayoutSpec.h rename to Source/Layout/ASLayoutSpec.h index 42ec34bbb1..4a28709d56 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/Source/Layout/ASLayoutSpec.h @@ -34,10 +34,10 @@ NS_ASSUME_NONNULL_BEGIN * only require a single child. * * For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example) - * a subclass should use this method to set the "primary" child. It can then use setChild:forIdentifier: + * a subclass should use this method to set the "primary" child. It can then use setChild:atIndex: * to set any other required children. Ideally a subclass would hide this from the user, and use the - * setChild:forIdentifier: internally. For example, ASBackgroundLayoutSpec exposes a backgroundChild - * property that behind the scenes is calling setChild:forIdentifier:. + * setChild:atIndex: internally. For example, ASBackgroundLayoutSpec exposes a "background" + * property that behind the scenes is calling setChild:atIndex:. */ @property (nullable, strong, nonatomic) id child; diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/Source/Layout/ASLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASLayoutSpec.mm rename to Source/Layout/ASLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h b/Source/Layout/ASOverlayLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASOverlayLayoutSpec.h rename to Source/Layout/ASOverlayLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/Source/Layout/ASOverlayLayoutSpec.mm similarity index 87% rename from AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm rename to Source/Layout/ASOverlayLayoutSpec.mm index 751ab1cda5..988ddc820c 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/Source/Layout/ASOverlayLayoutSpec.mm @@ -31,14 +31,24 @@ static NSUInteger const kOverlayChildIndex = 1; if (!(self = [super init])) { return nil; } - ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); - [self setChild:child atIndex:kUnderlayChildIndex]; + self.child = child; self.overlay = overlay; return self; } #pragma mark - Setter / Getter +- (void)setChild:(id)child +{ + ASDisplayNodeAssertNotNil(child, @"Child that will be overlayed on shouldn't be nil"); + [super setChild:child atIndex:kUnderlayChildIndex]; +} + +- (id)child +{ + return [super childAtIndex:kUnderlayChildIndex]; +} + - (void)setOverlay:(id)overlay { ASDisplayNodeAssertNotNil(overlay, @"Overlay cannot be nil"); @@ -59,7 +69,7 @@ static NSUInteger const kOverlayChildIndex = 1; restrictedToSize:(ASLayoutElementSize)size relativeToParentSize:(CGSize)parentSize { - ASLayout *contentsLayout = [[super childAtIndex:kUnderlayChildIndex] layoutThatFits:constrainedSize parentSize:parentSize]; + ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize]; contentsLayout.position = CGPointZero; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; if (self.overlay) { diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.h b/Source/Layout/ASRatioLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASRatioLayoutSpec.h rename to Source/Layout/ASRatioLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/Source/Layout/ASRatioLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm rename to Source/Layout/ASRatioLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h b/Source/Layout/ASRelativeLayoutSpec.h similarity index 100% rename from AsyncDisplayKit/Layout/ASRelativeLayoutSpec.h rename to Source/Layout/ASRelativeLayoutSpec.h diff --git a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm b/Source/Layout/ASRelativeLayoutSpec.mm similarity index 100% rename from AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm rename to Source/Layout/ASRelativeLayoutSpec.mm diff --git a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h b/Source/Layout/ASStackLayoutDefines.h similarity index 92% rename from AsyncDisplayKit/Layout/ASStackLayoutDefines.h rename to Source/Layout/ASStackLayoutDefines.h index f19372e383..79281d73dc 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutDefines.h +++ b/Source/Layout/ASStackLayoutDefines.h @@ -66,6 +66,7 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) { ASStackLayoutAlignItemsBaselineFirst, /** Children align to their last baseline. Only available for horizontal stack spec */ ASStackLayoutAlignItemsBaselineLast, + ASStackLayoutAlignItemsNotSet }; /** @@ -85,6 +86,22 @@ typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) { ASStackLayoutAlignSelfStretch, }; +// TODO documentation +typedef NS_ENUM(NSUInteger, ASStackLayoutFlexWrap) { + ASStackLayoutFlexWrapNoWrap, + ASStackLayoutFlexWrapWrap, +}; + +// TODO documentation +typedef NS_ENUM(NSUInteger, ASStackLayoutAlignContent) { + ASStackLayoutAlignContentStart, + ASStackLayoutAlignContentCenter, + ASStackLayoutAlignContentEnd, + ASStackLayoutAlignContentSpaceBetween, + ASStackLayoutAlignContentSpaceAround, + ASStackLayoutAlignContentStretch, +}; + /** Orientation of children along horizontal axis */ typedef NS_ENUM(NSUInteger, ASHorizontalAlignment) { /** No alignment specified. Default value */ diff --git a/AsyncDisplayKit/Layout/ASStackLayoutElement.h b/Source/Layout/ASStackLayoutElement.h similarity index 100% rename from AsyncDisplayKit/Layout/ASStackLayoutElement.h rename to Source/Layout/ASStackLayoutElement.h diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h b/Source/Layout/ASStackLayoutSpec.h similarity index 71% rename from AsyncDisplayKit/Layout/ASStackLayoutSpec.h rename to Source/Layout/ASStackLayoutSpec.h index d805f1e9dc..d218db7b42 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.h +++ b/Source/Layout/ASStackLayoutSpec.h @@ -59,6 +59,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) ASStackLayoutJustifyContent justifyContent; /** Orientation of children along cross axis. Defaults to ASStackLayoutAlignItemsStretch */ @property (nonatomic, assign) ASStackLayoutAlignItems alignItems; +//TODO documentation. Defaults to ASStackLayoutFlexWrapNoWrap +@property (nonatomic, assign) ASStackLayoutFlexWrap flexWrap; +//TODO documentation. Defaults to ASStackLayoutAlignContentStart +@property (nonatomic, assign) ASStackLayoutAlignContent alignContent; - (instancetype)init; @@ -69,7 +73,27 @@ NS_ASSUME_NONNULL_BEGIN @param alignItems Orientation of the children along the cross axis @param children ASLayoutElement children to be positioned. */ -+ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray> *)children AS_WARN_UNUSED_RESULT; ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + children:(NSArray> *)children AS_WARN_UNUSED_RESULT; + +/** + @param direction The direction of the stack view (horizontal or vertical) + @param spacing The spacing between the children + @param justifyContent If no children are flexible, this describes how to fill any extra space + @param alignItems Orientation of the children along the cross axis + @param children ASLayoutElement children to be positioned. + TODO documentation flex wrap and align content + */ ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction + spacing:(CGFloat)spacing + justifyContent:(ASStackLayoutJustifyContent)justifyContent + alignItems:(ASStackLayoutAlignItems)alignItems + flexWrap:(ASStackLayoutFlexWrap)flexWrap + alignContent:(ASStackLayoutAlignContent)alignContent + children:(NSArray> *)children AS_WARN_UNUSED_RESULT; /** * @return A stack layout spec with direction of ASStackLayoutDirectionVertical diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/Source/Layout/ASStackLayoutSpec.mm similarity index 82% rename from AsyncDisplayKit/Layout/ASStackLayoutSpec.mm rename to Source/Layout/ASStackLayoutSpec.mm index 1089592f91..1a78a5428c 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/Source/Layout/ASStackLayoutSpec.mm @@ -25,12 +25,17 @@ - (instancetype)init { - return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:nil]; + return [self initWithDirection:ASStackLayoutDirectionHorizontal spacing:0.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart children:nil]; } + (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children { - return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems children:children]; + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:ASStackLayoutFlexWrapNoWrap alignContent:ASStackLayoutAlignContentStart children:children]; +} + ++ (instancetype)stackLayoutSpecWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent children:(NSArray> *)children +{ + return [[self alloc] initWithDirection:direction spacing:spacing justifyContent:justifyContent alignItems:alignItems flexWrap:flexWrap alignContent:alignContent children:children]; } + (instancetype)verticalStackLayoutSpec @@ -47,7 +52,7 @@ return stackLayoutSpec; } -- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children +- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems flexWrap:(ASStackLayoutFlexWrap)flexWrap alignContent:(ASStackLayoutAlignContent)alignContent children:(NSArray *)children { if (!(self = [super init])) { return nil; @@ -58,6 +63,8 @@ _verticalAlignment = ASVerticalAlignmentNone; _alignItems = alignItems; _justifyContent = justifyContent; + _flexWrap = flexWrap; + _alignContent = alignContent; [self setChildren:children]; return self; @@ -127,7 +134,7 @@ return {child, style, style.size}; }); - const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems}; + const ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .flexWrap = _flexWrap, .alignContent = _alignContent}; const auto unpositionedLayout = ASStackUnpositionedLayout::compute(stackChildren, style, constrainedSize); const auto positionedLayout = ASStackPositionedLayout::compute(unpositionedLayout, style, constrainedSize); @@ -137,14 +144,12 @@ self.style.descender = stackChildren.back().style.descender; } - const CGSize finalSize = directionSize(style.direction, unpositionedLayout.stackDimensionSum, unpositionedLayout.crossSize); - NSMutableArray *sublayouts = [NSMutableArray array]; - for (const auto &l : positionedLayout.items) { - [sublayouts addObject:l.layout]; + for (const auto &item : positionedLayout.items) { + [sublayouts addObject:item.layout]; } - - return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts]; + + return [ASLayout layoutWithLayoutElement:self size:positionedLayout.size sublayouts:sublayouts]; } - (void)resolveHorizontalAlignment diff --git a/AsyncDisplayKit/Private/ASBasicImageDownloaderInternal.h b/Source/Private/ASBasicImageDownloaderInternal.h similarity index 100% rename from AsyncDisplayKit/Private/ASBasicImageDownloaderInternal.h rename to Source/Private/ASBasicImageDownloaderInternal.h diff --git a/AsyncDisplayKit/Private/ASBatchFetching.h b/Source/Private/ASBatchFetching.h similarity index 100% rename from AsyncDisplayKit/Private/ASBatchFetching.h rename to Source/Private/ASBatchFetching.h diff --git a/AsyncDisplayKit/Private/ASBatchFetching.m b/Source/Private/ASBatchFetching.m similarity index 100% rename from AsyncDisplayKit/Private/ASBatchFetching.m rename to Source/Private/ASBatchFetching.m diff --git a/AsyncDisplayKit/ASCellNode+Internal.h b/Source/Private/ASCellNode+Internal.h similarity index 87% rename from AsyncDisplayKit/ASCellNode+Internal.h rename to Source/Private/ASCellNode+Internal.h index ddfacd1ae8..57eeacb2b8 100644 --- a/AsyncDisplayKit/ASCellNode+Internal.h +++ b/Source/Private/ASCellNode+Internal.h @@ -15,6 +15,8 @@ NS_ASSUME_NONNULL_BEGIN +@class ASCollectionElement; + @protocol ASCellNodeInteractionDelegate /** @@ -30,6 +32,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)nodeDidRelayout:(ASCellNode *)node sizeChanged:(BOOL)sizeChanged; +/** + * Notifies the delegate that a specified cell node invalidates it's size what could result into a size change. + * + * @param node A node informing the delegate about the relayout. + */ +- (void)nodeDidInvalidateSize:(ASCellNode *)node; + /* * Methods to be called whenever the selection or highlight state changes * on ASCellNode. UIKit internally stores these values to update reusable cells. @@ -59,10 +68,7 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, strong, nullable) UICollectionViewLayoutAttributes *layoutAttributes; -/// readwrite variant of the readonly public property. -@property (nonatomic, copy, nullable) NSString *supplementaryElementKind; - -@property (nonatomic, copy, nullable) NSIndexPath *cachedIndexPath; +@property (weak, nullable) ASCollectionElement *collectionElement; @property (nonatomic, weak, nullable) ASDisplayNode *owningNode; diff --git a/AsyncDisplayKit/Private/ASCollectionView+Undeprecated.h b/Source/Private/ASCollectionView+Undeprecated.h similarity index 100% rename from AsyncDisplayKit/Private/ASCollectionView+Undeprecated.h rename to Source/Private/ASCollectionView+Undeprecated.h diff --git a/AsyncDisplayKit/Private/ASCollectionViewFlowLayoutInspector.h b/Source/Private/ASCollectionViewFlowLayoutInspector.h similarity index 100% rename from AsyncDisplayKit/Private/ASCollectionViewFlowLayoutInspector.h rename to Source/Private/ASCollectionViewFlowLayoutInspector.h diff --git a/AsyncDisplayKit/Private/ASCollectionViewFlowLayoutInspector.m b/Source/Private/ASCollectionViewFlowLayoutInspector.m similarity index 100% rename from AsyncDisplayKit/Private/ASCollectionViewFlowLayoutInspector.m rename to Source/Private/ASCollectionViewFlowLayoutInspector.m diff --git a/AsyncDisplayKit/Private/ASControlTargetAction.h b/Source/Private/ASControlTargetAction.h similarity index 100% rename from AsyncDisplayKit/Private/ASControlTargetAction.h rename to Source/Private/ASControlTargetAction.h diff --git a/AsyncDisplayKit/Private/ASControlTargetAction.m b/Source/Private/ASControlTargetAction.m similarity index 100% rename from AsyncDisplayKit/Private/ASControlTargetAction.m rename to Source/Private/ASControlTargetAction.m diff --git a/AsyncDisplayKit/Private/ASDefaultPlayButton.h b/Source/Private/ASDefaultPlayButton.h similarity index 100% rename from AsyncDisplayKit/Private/ASDefaultPlayButton.h rename to Source/Private/ASDefaultPlayButton.h diff --git a/AsyncDisplayKit/Private/ASDefaultPlayButton.m b/Source/Private/ASDefaultPlayButton.m similarity index 100% rename from AsyncDisplayKit/Private/ASDefaultPlayButton.m rename to Source/Private/ASDefaultPlayButton.m diff --git a/AsyncDisplayKit/Private/ASDefaultPlaybackButton.h b/Source/Private/ASDefaultPlaybackButton.h similarity index 100% rename from AsyncDisplayKit/Private/ASDefaultPlaybackButton.h rename to Source/Private/ASDefaultPlaybackButton.h diff --git a/AsyncDisplayKit/Private/ASDefaultPlaybackButton.m b/Source/Private/ASDefaultPlaybackButton.m similarity index 100% rename from AsyncDisplayKit/Private/ASDefaultPlaybackButton.m rename to Source/Private/ASDefaultPlaybackButton.m diff --git a/AsyncDisplayKit/Private/ASDispatch.h b/Source/Private/ASDispatch.h similarity index 100% rename from AsyncDisplayKit/Private/ASDispatch.h rename to Source/Private/ASDispatch.h diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/Source/Private/ASDisplayNode+AsyncDisplay.mm similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm rename to Source/Private/ASDisplayNode+AsyncDisplay.mm diff --git a/AsyncDisplayKit/Private/ASDisplayNode+DebugTiming.h b/Source/Private/ASDisplayNode+DebugTiming.h similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNode+DebugTiming.h rename to Source/Private/ASDisplayNode+DebugTiming.h diff --git a/AsyncDisplayKit/Private/ASDisplayNode+DebugTiming.mm b/Source/Private/ASDisplayNode+DebugTiming.mm similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNode+DebugTiming.mm rename to Source/Private/ASDisplayNode+DebugTiming.mm diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h b/Source/Private/ASDisplayNode+FrameworkPrivate.h similarity index 99% rename from AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h rename to Source/Private/ASDisplayNode+FrameworkPrivate.h index 7017192851..64bc453493 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkPrivate.h +++ b/Source/Private/ASDisplayNode+FrameworkPrivate.h @@ -226,7 +226,7 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyStat * @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes * size is invalidated and may need to result in a different size as the current calculated size. */ -- (void)_locked_displayNodeDidInvalidateSizeNewSize:(CGSize)newSize; +- (void)_locked_rootNodeDidInvalidateSize; @end diff --git a/AsyncDisplayKit/Private/ASDisplayNode+FrameworkSubclasses.h b/Source/Private/ASDisplayNode+FrameworkSubclasses.h similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNode+FrameworkSubclasses.h rename to Source/Private/ASDisplayNode+FrameworkSubclasses.h diff --git a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm b/Source/Private/ASDisplayNode+UIViewBridge.mm similarity index 99% rename from AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm rename to Source/Private/ASDisplayNode+UIViewBridge.mm index c4da0c0fb8..c26faba35c 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+UIViewBridge.mm +++ b/Source/Private/ASDisplayNode+UIViewBridge.mm @@ -255,6 +255,11 @@ if (shouldApply) { _layer.layerProperty = (layerValueExpr); } else { ASDisplayNo CGPoint newPosition = CGPointZero; ASBoundsAndPositionForFrame(rect, origin, anchorPoint, &newBounds, &newPosition); + if (ASIsCGRectValidForLayout(newBounds) == NO || ASIsCGPositionValidForLayout(newPosition) == NO) { + ASDisplayNodeAssertNonFatal(NO, @"-[ASDisplayNode setFrame:] - The new frame (%@) is invalid and unsafe to be set.", NSStringFromCGRect(rect)); + return; + } + if (useLayer) { layer.bounds = newBounds; layer.position = newPosition; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/Source/Private/ASDisplayNodeInternal.h similarity index 97% rename from AsyncDisplayKit/Private/ASDisplayNodeInternal.h rename to Source/Private/ASDisplayNodeInternal.h index aee6b2655b..69e7ed084f 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/Source/Private/ASDisplayNodeInternal.h @@ -180,6 +180,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo YGNodeRef _yogaNode; ASDisplayNode *_yogaParent; NSMutableArray *_yogaChildren; + ASLayout *_yogaCalculatedLayout; #endif #if TIME_DISPLAYNODE_OPS @@ -283,6 +284,12 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo */ - (ASPrimitiveTraitCollection)primitiveTraitCollection; +/** + * This is a non-deprecated internal declaration of the property. Public declaration + * is in ASDisplayNode+Beta.h + */ +@property (nonatomic, assign) BOOL shouldRasterizeDescendants; + @end NS_ASSUME_NONNULL_END diff --git a/AsyncDisplayKit/Private/ASDisplayNodeLayout.h b/Source/Private/ASDisplayNodeLayout.h similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNodeLayout.h rename to Source/Private/ASDisplayNodeLayout.h diff --git a/AsyncDisplayKit/Private/ASDisplayNodeLayout.mm b/Source/Private/ASDisplayNodeLayout.mm similarity index 100% rename from AsyncDisplayKit/Private/ASDisplayNodeLayout.mm rename to Source/Private/ASDisplayNodeLayout.mm diff --git a/Source/Private/ASElementMap.h b/Source/Private/ASElementMap.h new file mode 100644 index 0000000000..9b0bcb2d09 --- /dev/null +++ b/Source/Private/ASElementMap.h @@ -0,0 +1,90 @@ +// +// ASElementMap.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/22/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASCollectionElement, ASSection; +@protocol ASSectionContext; + +AS_SUBCLASSING_RESTRICTED +@interface ASElementMap : NSObject + +/** + * The number of sections (of items) in this map. + */ +@property (readonly) NSInteger numberOfSections; + +/** + * Returns number of items in the given section. O(1) + */ +- (NSInteger)numberOfItemsInSection:(NSInteger)section; + +/** + * Returns the context object for the given section, if any. O(1) + */ +- (nullable id)contextForSection:(NSInteger)section; + +/** + * All the index paths for all the items in this map. O(N) + * + * This property may be removed in the future, since it doesn't account for supplementary nodes. + */ +@property (copy, readonly) NSArray *itemIndexPaths; + +/** + * Returns the index path that corresponds to the same element in @c map at the given @c indexPath. O(1) + */ +- (nullable NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map; + +/** + * Returns the index path for the given element. O(1) + */ +- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element; + +/** + * Returns the item-element at the given index path. O(1) + */ +- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath; + +/** + * Returns the element for the supplementary element of the given kind at the given index path. O(1) + */ +- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath; + +/** + * Enumerates all the elements in this map, and their index paths. + */ +- (void)enumerateUsingBlock:(AS_NOESCAPE void(^)(NSIndexPath *indexPath, ASCollectionElement *element, BOOL *stop))block; + + +#pragma mark - Initialization -- Only Useful to ASDataController + + +// SectionIndex -> ItemIndex -> Element +typedef NSArray *> ASCollectionElementTwoDimensionalArray; + +// ElementKind -> IndexPath -> Element +typedef NSDictionary *> ASSupplementaryElementDictionary; + +/** + * Create a new element map for this dataset. You probably don't need to use this – ASDataController is the only one who creates these. + * + * @param sections The array of ASSection objects. + * @param items A 2D array of ASCollectionElements, for each item. + * @param supplementaryElements A dictionary of gathered supplementary elements. + */ +- (instancetype)initWithSections:(NSArray *)sections + items:(ASCollectionElementTwoDimensionalArray *)items + supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASElementMap.m b/Source/Private/ASElementMap.m new file mode 100644 index 0000000000..3fed5e9f48 --- /dev/null +++ b/Source/Private/ASElementMap.m @@ -0,0 +1,151 @@ +// +// ASElementMap.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/22/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "ASElementMap.h" +#import +#import +#import +#import +#import +#import + +@interface ASElementMap () + +@property (nonatomic, strong, readonly) NSArray *sections; + +// Element -> IndexPath +@property (nonatomic, strong, readonly) NSMapTable *elementToIndexPathMap; + +// The items, in a 2D array +@property (nonatomic, strong, readonly) ASCollectionElementTwoDimensionalArray *sectionsOfItems; + +@property (nonatomic, strong, readonly) ASSupplementaryElementDictionary *supplementaryElements; + +@end + +@implementation ASElementMap + +- (instancetype)init +{ + return [self initWithSections:@[] items:@[] supplementaryElements:@{}]; +} + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements +{ + if (self = [super init]) { + _sections = [sections copy]; + _sectionsOfItems = [[NSArray alloc] initWithArray:items copyItems:YES]; + _supplementaryElements = [[NSDictionary alloc] initWithDictionary:supplementaryElements copyItems:YES]; + + // Setup our index path map + _elementToIndexPathMap = [NSMapTable mapTableWithKeyOptions:(NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableCopyIn]; + NSInteger s = 0; + for (NSArray *section in _sectionsOfItems) { + NSInteger i = 0; + for (ASCollectionElement *element in section) { + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s]; + [_elementToIndexPathMap setObject:indexPath forKey:element]; + i++; + } + s++; + } + for (NSDictionary *supplementariesForKind in [_supplementaryElements objectEnumerator]) { + [supplementariesForKind enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *_Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) { + [_elementToIndexPathMap setObject:indexPath forKey:element]; + }]; + } + } + return self; +} + +- (NSArray *)itemIndexPaths +{ + return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems); +} + +- (NSInteger)numberOfSections +{ + return _sectionsOfItems.count; +} + +- (NSInteger)numberOfItemsInSection:(NSInteger)section +{ + return _sectionsOfItems[section].count; +} + +- (id)contextForSection:(NSInteger)section +{ + return _sections[section].context; +} + +- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element +{ + return [_elementToIndexPathMap objectForKey:element]; +} + +- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath +{ + return (indexPath != nil) ? ASGetElementInTwoDimensionalArray(_sectionsOfItems, indexPath) : nil; +} + +- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath +{ + return _supplementaryElements[supplementaryElementKind][indexPath]; +} + +- (NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map +{ + id element = [map elementForItemAtIndexPath:indexPath]; + return [self indexPathForElement:element]; +} + +- (void)enumerateUsingBlock:(void(^)(NSIndexPath *indexPath, ASCollectionElement *element, BOOL *stop))block +{ + __block BOOL stop = NO; + + // Do items first + for (NSArray *section in _sectionsOfItems) { + for (ASCollectionElement *element in section) { + NSIndexPath *indexPath = [self indexPathForElement:element]; + block(indexPath, element, &stop); + if (stop) { + return; + } + } + } + + // Then supplementaries + [_supplementaryElements enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull kind, NSDictionary * _Nonnull elementsOfKind, BOOL * _Nonnull stop0) { + [elementsOfKind enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop1) { + block(indexPath, element, &stop); + if (stop) { + *stop1 = YES; + } + }]; + if (stop) { + *stop0 = YES; + } + }]; +} + +#pragma mark - NSCopying + +- (id)copyWithZone:(NSZone *)zone +{ + return self; +} + +// NSMutableCopying conformance is declared in ASMutableElementMap.h, so that most consumers of ASElementMap don't bother with it. +#pragma mark - NSMutableCopying + +- (id)mutableCopyWithZone:(NSZone *)zone +{ + return [[ASMutableElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements]; +} + +@end diff --git a/AsyncDisplayKit/Private/ASIGListAdapterBasedDataSource.h b/Source/Private/ASIGListAdapterBasedDataSource.h similarity index 100% rename from AsyncDisplayKit/Private/ASIGListAdapterBasedDataSource.h rename to Source/Private/ASIGListAdapterBasedDataSource.h diff --git a/AsyncDisplayKit/Private/ASIGListAdapterBasedDataSource.m b/Source/Private/ASIGListAdapterBasedDataSource.m similarity index 98% rename from AsyncDisplayKit/Private/ASIGListAdapterBasedDataSource.m rename to Source/Private/ASIGListAdapterBasedDataSource.m index fded34591d..9b0717a499 100644 --- a/AsyncDisplayKit/Private/ASIGListAdapterBasedDataSource.m +++ b/Source/Private/ASIGListAdapterBasedDataSource.m @@ -199,9 +199,9 @@ typedef struct { } } -- (ASCellNode *)collectionNode:(ASCollectionNode *)collectionNode nodeForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { - return [[self supplementaryElementSourceForSection:indexPath.section] nodeForSupplementaryElementOfKind:kind atIndex:indexPath.item]; + return [[self supplementaryElementSourceForSection:indexPath.section] nodeBlockForSupplementaryElementOfKind:kind atIndex:indexPath.item]; } - (NSArray *)collectionNode:(ASCollectionNode *)collectionNode supplementaryElementKindsInSection:(NSInteger)section diff --git a/AsyncDisplayKit/Private/ASImageNode+AnimatedImagePrivate.h b/Source/Private/ASImageNode+AnimatedImagePrivate.h similarity index 100% rename from AsyncDisplayKit/Private/ASImageNode+AnimatedImagePrivate.h rename to Source/Private/ASImageNode+AnimatedImagePrivate.h diff --git a/AsyncDisplayKit/Private/ASImageNode+CGExtras.h b/Source/Private/ASImageNode+CGExtras.h similarity index 100% rename from AsyncDisplayKit/Private/ASImageNode+CGExtras.h rename to Source/Private/ASImageNode+CGExtras.h diff --git a/AsyncDisplayKit/Private/ASImageNode+CGExtras.m b/Source/Private/ASImageNode+CGExtras.m similarity index 100% rename from AsyncDisplayKit/Private/ASImageNode+CGExtras.m rename to Source/Private/ASImageNode+CGExtras.m diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/Source/Private/ASInternalHelpers.h similarity index 100% rename from AsyncDisplayKit/Private/ASInternalHelpers.h rename to Source/Private/ASInternalHelpers.h diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.m b/Source/Private/ASInternalHelpers.m similarity index 100% rename from AsyncDisplayKit/Private/ASInternalHelpers.m rename to Source/Private/ASInternalHelpers.m diff --git a/AsyncDisplayKit/Private/ASLayoutTransition.h b/Source/Private/ASLayoutTransition.h similarity index 100% rename from AsyncDisplayKit/Private/ASLayoutTransition.h rename to Source/Private/ASLayoutTransition.h diff --git a/AsyncDisplayKit/Private/ASLayoutTransition.mm b/Source/Private/ASLayoutTransition.mm similarity index 100% rename from AsyncDisplayKit/Private/ASLayoutTransition.mm rename to Source/Private/ASLayoutTransition.mm diff --git a/AsyncDisplayKit/Private/ASMultidimensionalArrayUtils.h b/Source/Private/ASMultidimensionalArrayUtils.h similarity index 100% rename from AsyncDisplayKit/Private/ASMultidimensionalArrayUtils.h rename to Source/Private/ASMultidimensionalArrayUtils.h diff --git a/AsyncDisplayKit/Private/ASMultidimensionalArrayUtils.mm b/Source/Private/ASMultidimensionalArrayUtils.mm similarity index 99% rename from AsyncDisplayKit/Private/ASMultidimensionalArrayUtils.mm rename to Source/Private/ASMultidimensionalArrayUtils.mm index 985b4cd5cb..bfa6c65eb4 100644 --- a/AsyncDisplayKit/Private/ASMultidimensionalArrayUtils.mm +++ b/Source/Private/ASMultidimensionalArrayUtils.mm @@ -226,6 +226,7 @@ NSArray *ASIndexPathsForMultidimensionalArray(NSArray *multidimensionalArray) id ASGetElementInTwoDimensionalArray(NSArray *array, NSIndexPath *indexPath) { + ASDisplayNodeCAssertNotNil(indexPath, @"Expected non-nil index path"); ASDisplayNodeCAssert(indexPath.length == 2, @"Expected index path of length 2. Index path: %@", indexPath); NSInteger section = indexPath.section; if (array.count <= section) { diff --git a/Source/Private/ASMutableElementMap.h b/Source/Private/ASMutableElementMap.h new file mode 100644 index 0000000000..221353e573 --- /dev/null +++ b/Source/Private/ASMutableElementMap.h @@ -0,0 +1,52 @@ +// +// ASMutableElementMap.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/23/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@class ASSection, ASCollectionElement; + +/** + * This mutable version will be removed in the future. It's only here now to keep the diff small + * as we port data controller to use ASElementMap. + */ +AS_SUBCLASSING_RESTRICTED +@interface ASMutableElementMap : NSObject + +- (instancetype)init __unavailable; + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements; + +- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index; + +- (void)removeAllSectionContexts; + +/// Only modifies the array of ASSection * objects +- (void)removeSectionContextsAtIndexes:(NSIndexSet *)indexes; + +- (void)removeAllElements; + +- (void)removeItemsAtIndexPaths:(NSArray *)indexPaths; + +- (void)removeSectionsOfItems:(NSIndexSet *)itemSections; + +- (void)removeSupplementaryElementsInSections:(NSIndexSet *)sections; + +- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections; + +- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath; + +@end + +@interface ASElementMap (MutableCopying) +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASMutableElementMap.m b/Source/Private/ASMutableElementMap.m new file mode 100644 index 0000000000..40b90ca967 --- /dev/null +++ b/Source/Private/ASMutableElementMap.m @@ -0,0 +1,115 @@ +// +// ASMutableElementMap.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/23/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "ASMutableElementMap.h" + +#import +#import +#import +#import +#import + +typedef NSMutableArray *> ASMutableCollectionElementTwoDimensionalArray; + +typedef NSMutableDictionary *> ASMutableSupplementaryElementDictionary; + +@implementation ASMutableElementMap { + ASMutableSupplementaryElementDictionary *_supplementaryElements; + NSMutableArray *_sections; + ASMutableCollectionElementTwoDimensionalArray *_sectionsOfItems; +} + +- (instancetype)initWithSections:(NSArray *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements +{ + if (self = [super init]) { + _sections = [sections mutableCopy]; + _sectionsOfItems = (id)ASTwoDimensionalArrayDeepMutableCopy(items); + _supplementaryElements = [ASMutableElementMap deepMutableCopyOfElementsDictionary:supplementaryElements]; + } + return self; +} + +- (id)copyWithZone:(NSZone *)zone +{ + return [[ASElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements]; +} + +- (void)removeAllSectionContexts +{ + [_sections removeAllObjects]; +} + +- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index +{ + [_sections insertObject:section atIndex:index]; +} + +- (void)removeItemsAtIndexPaths:(NSArray *)indexPaths +{ + indexPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)]; + ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_sectionsOfItems, indexPaths); +} + +- (void)removeSectionContextsAtIndexes:(NSIndexSet *)indexes +{ + [_sections removeObjectsAtIndexes:indexes]; +} + +- (void)removeAllElements +{ + [_sectionsOfItems removeAllObjects]; + [_supplementaryElements removeAllObjects]; +} + +- (void)removeSectionsOfItems:(NSIndexSet *)itemSections +{ + [_sectionsOfItems removeObjectsAtIndexes:itemSections]; +} + +- (void)removeSupplementaryElementsInSections:(NSIndexSet *)sections +{ + [_supplementaryElements enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableDictionary * _Nonnull supplementariesForKind, BOOL * _Nonnull stop) { + [supplementariesForKind removeObjectsForKeys:[sections as_filterIndexPathsBySection:supplementariesForKind]]; + }]; +} + +- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections +{ + [sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) { + [_sectionsOfItems insertObject:[NSMutableArray array] atIndex:idx]; + }]; +} + +- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath +{ + NSString *kind = element.supplementaryElementKind; + if (kind == nil) { + [_sectionsOfItems[indexPath.section] insertObject:element atIndex:indexPath.item]; + } else { + NSMutableDictionary *supplementariesForKind = _supplementaryElements[kind]; + if (supplementariesForKind == nil) { + supplementariesForKind = [NSMutableDictionary dictionary]; + _supplementaryElements[kind] = supplementariesForKind; + } + supplementariesForKind[indexPath] = element; + } +} + +#pragma mark - Helpers + ++ (ASMutableSupplementaryElementDictionary *)deepMutableCopyOfElementsDictionary:(ASSupplementaryElementDictionary *)originalDict +{ + NSMutableDictionary *deepCopy = [NSMutableDictionary dictionaryWithCapacity:originalDict.count]; + [originalDict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDictionary * _Nonnull obj, BOOL * _Nonnull stop) { + deepCopy[key] = [obj mutableCopy]; + }]; + + return deepCopy; +} + +@end diff --git a/AsyncDisplayKit/Private/ASPendingStateController.h b/Source/Private/ASPendingStateController.h similarity index 100% rename from AsyncDisplayKit/Private/ASPendingStateController.h rename to Source/Private/ASPendingStateController.h diff --git a/AsyncDisplayKit/Private/ASPendingStateController.mm b/Source/Private/ASPendingStateController.mm similarity index 100% rename from AsyncDisplayKit/Private/ASPendingStateController.mm rename to Source/Private/ASPendingStateController.mm diff --git a/Source/Private/ASRectTable.h b/Source/Private/ASRectTable.h new file mode 100644 index 0000000000..3617f44b17 --- /dev/null +++ b/Source/Private/ASRectTable.h @@ -0,0 +1,64 @@ +// +// ASRectTable.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/24/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + * An alias for an NSMapTable created to store rects. + * + * You should not call -objectForKey:, -setObject:forKey:, or -allObjects + * on these objects. + */ +typedef NSMapTable ASRectTable; + +/** + * A category for creating & using map tables meant for storing CGRects. + * + * This category is private, so name collisions are not worth worrying about. + */ +@interface NSMapTable (ASRectTableMethods) + +/** + * Creates a new rect table with (NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) for keys. + */ ++ (ASRectTable *)rectTableForStrongObjectPointers; + +/** + * Creates a new rect table with (NSMapTableWeakMemory | NSMapTableObjectPointerPersonality) for keys. + */ ++ (ASRectTable *)rectTableForWeakObjectPointers; + +/** + * Retrieves the rect for a given key, or CGRectNull if the key is not found. + * + * @param key An object to lookup the rect for. + */ +- (CGRect)rectForKey:(KeyType)key; + +/** + * Sets the given rect for the associated key. + * + * @param rect The rect to store as value. + * @param key The key to use for the rect. + */ +- (void)setRect:(CGRect)rect forKey:(KeyType)key; + +/** + * Removes the rect for the given key, if one exists. + * + * @param key The key to remove. + */ +- (void)removeRectForKey:(KeyType)key; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASRectTable.m b/Source/Private/ASRectTable.m new file mode 100644 index 0000000000..1bd32fb53f --- /dev/null +++ b/Source/Private/ASRectTable.m @@ -0,0 +1,71 @@ +// +// ASRectTable.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/24/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "ASRectTable.h" + +__attribute__((const)) +static NSUInteger ASRectSize(const void *ptr) +{ + return sizeof(CGRect); +} + +@implementation NSMapTable (ASRectTableMethods) + ++ (instancetype)rectTableWithKeyPointerFunctions:(NSPointerFunctions *)keyFuncs +{ + static NSPointerFunctions *cgRectFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + cgRectFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStructPersonality | NSPointerFunctionsCopyIn | NSPointerFunctionsMallocMemory]; + cgRectFuncs.sizeFunction = &ASRectSize; + }); + + return [[NSMapTable alloc] initWithKeyPointerFunctions:keyFuncs valuePointerFunctions:cgRectFuncs capacity:0]; +} + ++ (instancetype)rectTableForStrongObjectPointers +{ + static NSPointerFunctions *strongObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + strongObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality]; + }); + return [self rectTableWithKeyPointerFunctions:strongObjectPointerFuncs]; +} + ++ (instancetype)rectTableForWeakObjectPointers +{ + static NSPointerFunctions *weakObjectPointerFuncs; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + weakObjectPointerFuncs = [NSPointerFunctions pointerFunctionsWithOptions:NSPointerFunctionsWeakMemory | NSPointerFunctionsObjectPointerPersonality]; + }); + return [self rectTableWithKeyPointerFunctions:weakObjectPointerFuncs]; +} + +- (CGRect)rectForKey:(id)key +{ + CGRect *ptr = (__bridge CGRect *)[self objectForKey:key]; + if (ptr == NULL) { + return CGRectNull; + } + return *ptr; +} + +- (void)setRect:(CGRect)rect forKey:(id)key +{ + __unsafe_unretained id obj = (__bridge id)▭ + [self setObject:obj forKey:key]; +} + +- (void)removeRectForKey:(id)key +{ + [self removeObjectForKey:key]; +} + +@end diff --git a/Source/Private/ASResponderChainEnumerator.h b/Source/Private/ASResponderChainEnumerator.h new file mode 100644 index 0000000000..4e292edab8 --- /dev/null +++ b/Source/Private/ASResponderChainEnumerator.h @@ -0,0 +1,28 @@ +// +// ASResponderChainEnumerator.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/13/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +AS_SUBCLASSING_RESTRICTED +@interface ASResponderChainEnumerator : NSEnumerator + +- (instancetype)initWithResponder:(UIResponder *)responder; + +@end + +@interface UIResponder (ASResponderChainEnumerator) + +- (ASResponderChainEnumerator *)asdk_responderChainEnumerator; + +@end + + +NS_ASSUME_NONNULL_END diff --git a/Source/Private/ASResponderChainEnumerator.m b/Source/Private/ASResponderChainEnumerator.m new file mode 100644 index 0000000000..2310c5c861 --- /dev/null +++ b/Source/Private/ASResponderChainEnumerator.m @@ -0,0 +1,41 @@ +// +// ASResponderChainEnumerator.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/13/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "ASResponderChainEnumerator.h" + +@implementation ASResponderChainEnumerator { + UIResponder *_currentResponder; +} + +- (instancetype)initWithResponder:(UIResponder *)responder +{ + if (self = [super init]) { + _currentResponder = responder; + } + return self; +} + +#pragma mark - NSEnumerator + +- (id)nextObject +{ + id result = [_currentResponder nextResponder]; + _currentResponder = result; + return result; +} + +@end + +@implementation UIResponder (ASResponderChainEnumerator) + +- (NSEnumerator *)asdk_responderChainEnumerator +{ + return [[ASResponderChainEnumerator alloc] initWithResponder:self]; +} + +@end diff --git a/AsyncDisplayKit/Private/ASSection.h b/Source/Private/ASSection.h similarity index 100% rename from AsyncDisplayKit/Private/ASSection.h rename to Source/Private/ASSection.h diff --git a/AsyncDisplayKit/Private/ASSection.m b/Source/Private/ASSection.m similarity index 100% rename from AsyncDisplayKit/Private/ASSection.m rename to Source/Private/ASSection.m diff --git a/AsyncDisplayKit/Private/ASTableView+Undeprecated.h b/Source/Private/ASTableView+Undeprecated.h similarity index 100% rename from AsyncDisplayKit/Private/ASTableView+Undeprecated.h rename to Source/Private/ASTableView+Undeprecated.h diff --git a/AsyncDisplayKit/Private/ASWeakMap.h b/Source/Private/ASWeakMap.h similarity index 100% rename from AsyncDisplayKit/Private/ASWeakMap.h rename to Source/Private/ASWeakMap.h diff --git a/AsyncDisplayKit/Private/ASWeakMap.m b/Source/Private/ASWeakMap.m similarity index 100% rename from AsyncDisplayKit/Private/ASWeakMap.m rename to Source/Private/ASWeakMap.m diff --git a/AsyncDisplayKit/Private/Layout/ASLayoutElementStylePrivate.h b/Source/Private/Layout/ASLayoutElementStylePrivate.h similarity index 100% rename from AsyncDisplayKit/Private/Layout/ASLayoutElementStylePrivate.h rename to Source/Private/Layout/ASLayoutElementStylePrivate.h diff --git a/AsyncDisplayKit/Private/Layout/ASLayoutSpecPrivate.h b/Source/Private/Layout/ASLayoutSpecPrivate.h similarity index 100% rename from AsyncDisplayKit/Private/Layout/ASLayoutSpecPrivate.h rename to Source/Private/Layout/ASLayoutSpecPrivate.h diff --git a/AsyncDisplayKit/Private/Layout/ASLayoutSpecUtilities.h b/Source/Private/Layout/ASLayoutSpecUtilities.h similarity index 100% rename from AsyncDisplayKit/Private/Layout/ASLayoutSpecUtilities.h rename to Source/Private/Layout/ASLayoutSpecUtilities.h diff --git a/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h b/Source/Private/Layout/ASStackLayoutSpecUtilities.h similarity index 94% rename from AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h rename to Source/Private/Layout/ASStackLayoutSpecUtilities.h index 6bee0a6866..fbcafdff41 100644 --- a/AsyncDisplayKit/Private/Layout/ASStackLayoutSpecUtilities.h +++ b/Source/Private/Layout/ASStackLayoutSpecUtilities.h @@ -15,6 +15,8 @@ typedef struct { CGFloat spacing; ASStackLayoutJustifyContent justifyContent; ASStackLayoutAlignItems alignItems; + ASStackLayoutFlexWrap flexWrap; + ASStackLayoutAlignContent alignContent; } ASStackLayoutSpecStyle; inline CGFloat stackDimension(const ASStackLayoutDirection direction, const CGSize size) @@ -42,6 +44,10 @@ inline CGSize directionSize(const ASStackLayoutDirection direction, const CGFloa return (direction == ASStackLayoutDirectionVertical) ? CGSizeMake(cross, stack) : CGSizeMake(stack, cross); } +inline void setStackValueToPoint(const ASStackLayoutDirection direction, const CGFloat stack, CGPoint &point) { + (direction == ASStackLayoutDirectionVertical) ? (point.y = stack) : (point.x = stack); +} + inline ASSizeRange directionSizeRange(const ASStackLayoutDirection direction, const CGFloat stackMin, const CGFloat stackMax, diff --git a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h b/Source/Private/Layout/ASStackPositionedLayout.h similarity index 94% rename from AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h rename to Source/Private/Layout/ASStackPositionedLayout.h index d4c7b3628a..dd0b697555 100644 --- a/AsyncDisplayKit/Private/Layout/ASStackPositionedLayout.h +++ b/Source/Private/Layout/ASStackPositionedLayout.h @@ -15,7 +15,9 @@ /** Represents a set of laid out and positioned stack layout children. */ struct ASStackPositionedLayout { const std::vector items; - + /** Final size of the stack */ + const CGSize size; + /** Given an unpositioned layout, computes the positions each child should be placed at. */ static ASStackPositionedLayout compute(const ASStackUnpositionedLayout &unpositionedLayout, const ASStackLayoutSpecStyle &style, diff --git a/Source/Private/Layout/ASStackPositionedLayout.mm b/Source/Private/Layout/ASStackPositionedLayout.mm new file mode 100644 index 0000000000..c39bdcdab6 --- /dev/null +++ b/Source/Private/Layout/ASStackPositionedLayout.mm @@ -0,0 +1,186 @@ +// +// ASStackPositionedLayout.mm +// AsyncDisplayKit +// +// 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. +// + +#import + +#import +#import + +#import +#import +#import +#import + +static CGFloat crossOffsetForItem(const ASStackLayoutSpecItem &item, + const ASStackLayoutSpecStyle &style, + const CGFloat crossSize, + const CGFloat baseline) +{ + switch (alignment(item.child.style.alignSelf, style.alignItems)) { + case ASStackLayoutAlignItemsEnd: + return crossSize - crossDimension(style.direction, item.layout.size); + case ASStackLayoutAlignItemsCenter: + return ASFloorPixelValue((crossSize - crossDimension(style.direction, item.layout.size)) / 2); + case ASStackLayoutAlignItemsBaselineFirst: + case ASStackLayoutAlignItemsBaselineLast: + return baseline - ASStackUnpositionedLayout::baselineForItem(style, item); + case ASStackLayoutAlignItemsStart: + case ASStackLayoutAlignItemsStretch: + case ASStackLayoutAlignItemsNotSet: + return 0; + } +} + +static void crossOffsetAndSpacingForEachLine(const std::size_t numOfLines, + const CGFloat crossViolation, + ASStackLayoutAlignContent alignContent, + CGFloat &offset, + CGFloat &spacing) +{ + ASDisplayNodeCAssertTrue(numOfLines > 0); + + // Handle edge cases + if (alignContent == ASStackLayoutAlignContentSpaceBetween && (crossViolation < kViolationEpsilon || numOfLines == 1)) { + alignContent = ASStackLayoutAlignContentStart; + } else if (alignContent == ASStackLayoutAlignContentSpaceAround && (crossViolation < kViolationEpsilon || numOfLines == 1)) { + alignContent = ASStackLayoutAlignContentCenter; + } + + offset = 0; + spacing = 0; + + switch (alignContent) { + case ASStackLayoutAlignContentCenter: + offset = crossViolation / 2; + break; + case ASStackLayoutAlignContentEnd: + offset = crossViolation; + break; + case ASStackLayoutAlignContentSpaceBetween: + // Spacing between the items, no spaces at the edges, evenly distributed + spacing = crossViolation / (numOfLines - 1); + break; + case ASStackLayoutAlignContentSpaceAround: { + // Spacing between items are twice the spacing on the edges + CGFloat spacingUnit = crossViolation / (numOfLines * 2); + offset = spacingUnit; + spacing = spacingUnit * 2; + break; + } + case ASStackLayoutAlignContentStart: + case ASStackLayoutAlignContentStretch: + break; + } +} + +static void stackOffsetAndSpacingForEachItem(const std::size_t numOfItems, + const CGFloat stackViolation, + ASStackLayoutJustifyContent justifyContent, + CGFloat &offset, + CGFloat &spacing) +{ + ASDisplayNodeCAssertTrue(numOfItems > 0); + + // Handle edge cases + if (justifyContent == ASStackLayoutJustifyContentSpaceBetween && (stackViolation < kViolationEpsilon || numOfItems == 1)) { + justifyContent = ASStackLayoutJustifyContentStart; + } else if (justifyContent == ASStackLayoutJustifyContentSpaceAround && (stackViolation < kViolationEpsilon || numOfItems == 1)) { + justifyContent = ASStackLayoutJustifyContentCenter; + } + + offset = 0; + spacing = 0; + + switch (justifyContent) { + case ASStackLayoutJustifyContentCenter: + offset = stackViolation / 2; + break; + case ASStackLayoutJustifyContentEnd: + offset = stackViolation; + break; + case ASStackLayoutJustifyContentSpaceBetween: + // Spacing between the items, no spaces at the edges, evenly distributed + spacing = stackViolation / (numOfItems - 1); + break; + case ASStackLayoutJustifyContentSpaceAround: { + // Spacing between items are twice the spacing on the edges + CGFloat spacingUnit = stackViolation / (numOfItems * 2); + offset = spacingUnit; + spacing = spacingUnit * 2; + break; + } + case ASStackLayoutJustifyContentStart: + break; + } +} + +static void positionItemsInLine(const ASStackUnpositionedLine &line, + const ASStackLayoutSpecStyle &style, + const CGPoint &startingPoint, + const CGFloat stackSpacing) +{ + CGPoint p = startingPoint; + BOOL first = YES; + + for (const auto &item : line.items) { + p = p + directionPoint(style.direction, item.child.style.spacingBefore, 0); + if (!first) { + p = p + directionPoint(style.direction, style.spacing + stackSpacing, 0); + } + first = NO; + item.layout.position = p + directionPoint(style.direction, 0, crossOffsetForItem(item, style, line.crossSize, line.baseline)); + + p = p + directionPoint(style.direction, stackDimension(style.direction, item.layout.size) + item.child.style.spacingAfter, 0); + } +} + +ASStackPositionedLayout ASStackPositionedLayout::compute(const ASStackUnpositionedLayout &layout, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const auto &lines = layout.lines; + if (lines.empty()) { + return {}; + } + + const auto numOfLines = lines.size(); + const auto direction = style.direction; + const auto alignContent = style.alignContent; + const auto justifyContent = style.justifyContent; + const auto crossViolation = ASStackUnpositionedLayout::computeCrossViolation(layout.crossDimensionSum, style, sizeRange); + CGFloat crossOffset; + CGFloat crossSpacing; + crossOffsetAndSpacingForEachLine(numOfLines, crossViolation, alignContent, crossOffset, crossSpacing); + + std::vector positionedItems; + CGPoint p = directionPoint(direction, 0, crossOffset); + BOOL first = YES; + for (const auto &line : lines) { + if (!first) { + p = p + directionPoint(direction, 0, crossSpacing); + } + first = NO; + + const auto &items = line.items; + const auto stackViolation = ASStackUnpositionedLayout::computeStackViolation(line.stackDimensionSum, style, sizeRange); + CGFloat stackOffset; + CGFloat stackSpacing; + stackOffsetAndSpacingForEachItem(items.size(), stackViolation, justifyContent, stackOffset, stackSpacing); + + setStackValueToPoint(direction, stackOffset, p); + positionItemsInLine(line, style, p, stackSpacing); + std::move(items.begin(), items.end(), std::back_inserter(positionedItems)); + + p = p + directionPoint(direction, -stackOffset, line.crossSize); + } + + const CGSize finalSize = directionSize(direction, layout.stackDimensionSum, layout.crossDimensionSum); + return {std::move(positionedItems), ASSizeRangeClamp(sizeRange, finalSize)}; +} diff --git a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h b/Source/Private/Layout/ASStackUnpositionedLayout.h similarity index 60% rename from AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h rename to Source/Private/Layout/ASStackUnpositionedLayout.h index af7bb8d0cd..e98852b948 100644 --- a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.h +++ b/Source/Private/Layout/ASStackUnpositionedLayout.h @@ -14,6 +14,9 @@ #import #import +/** The threshold that determines if a violation has actually occurred. */ +extern CGFloat const kViolationEpsilon; + struct ASStackLayoutSpecChild { /** The original source child. */ id element; @@ -30,19 +33,27 @@ struct ASStackLayoutSpecItem { ASLayout *layout; }; +struct ASStackUnpositionedLine { + /** The set of proposed children in this line, each contains child layout, not yet positioned. */ + std::vector items; + /** The total size of the children in the stack dimension, including all spacing. */ + CGFloat stackDimensionSum; + /** The size in the cross dimension */ + CGFloat crossSize; + /** The baseline of the stack which baseline aligned children should align to */ + CGFloat baseline; +}; /** Represents a set of stack layout children that have their final layout computed, but are not yet positioned. */ struct ASStackUnpositionedLayout { - /** A set of proposed child layouts, not yet positioned. */ - const std::vector items; - /** The total size of the children in the stack dimension, including all spacing. */ + /** The set of proposed lines, each contains child layouts, not yet positioned. */ + const std::vector lines; + /** + * In a single line stack (e.g no wrao), this is the total size of the children in the stack dimension, including all spacing. + * In a multi-line stack, this is the largest stack dimension among lines. + */ const CGFloat stackDimensionSum; - /** The amount by which stackDimensionSum violates constraints. If positive, less than min; negative, greater than max. */ - const CGFloat violation; - /** The size in the cross dimension */ - const CGFloat crossSize; - /** The baseline of the stack which baseline aligned children should align to */ - const CGFloat baseline; + const CGFloat crossDimensionSum; /** Given a set of children, computes the unpositioned layouts for those children. */ static ASStackUnpositionedLayout compute(const std::vector &children, @@ -51,4 +62,12 @@ struct ASStackUnpositionedLayout { static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecItem &l); + + static CGFloat computeStackViolation(const CGFloat stackDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange); + + static CGFloat computeCrossViolation(const CGFloat crossDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange); }; diff --git a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm b/Source/Private/Layout/ASStackUnpositionedLayout.mm similarity index 60% rename from AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm rename to Source/Private/Layout/ASStackUnpositionedLayout.mm index 7570ffd2d5..4ec2568591 100644 --- a/AsyncDisplayKit/Private/Layout/ASStackUnpositionedLayout.mm +++ b/Source/Private/Layout/ASStackUnpositionedLayout.mm @@ -16,6 +16,8 @@ #import #import +CGFloat const kViolationEpsilon = 0.01; + static CGFloat resolveCrossDimensionMaxForStretchChild(const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecChild &child, const CGFloat stackMax, @@ -65,8 +67,76 @@ static ASLayout *crossChildLayout(const ASStackLayoutSpecChild &child, return layout ? : [ASLayout layoutWithLayoutElement:child.element size:{0, 0}]; } -/** The threshold that determines if a violation has actually occurred. */ -static const CGFloat kViolationEpsilon = 0.01; +/** + Computes the consumed cross dimension length for the given vector of lines and stacking style. + + Cross Dimension + +---------------------> + +--------+ +--------+ +--------+ +---------+ + Vertical |Vertical| |Vertical| |Vertical| |Vertical | + Stack | Line 1 | | Line 2 | | Line 3 | | Line 4 | + | | | | | | | | + +--------+ +--------+ +--------+ +---------+ + crossDimensionSum + |------------------------------------------| + + @param lines unpositioned lines + */ +static CGFloat computeLinesCrossDimensionSum(const std::vector &lines) +{ + return std::accumulate(lines.begin(), lines.end(), 0.0, + [&](CGFloat x, const ASStackUnpositionedLine &l) { + return x + l.crossSize; + }); +} + + +/** + Computes the violation by comparing a cross dimension sum with the overall allowable size range for the stack. + + Violation is the distance you would have to add to the unbounded cross-direction length of the stack spec's + lines in order to bring the stack within its allowed sizeRange. The diagram below shows 3 vertical stacks, each contains 3-5 vertical lines, + with the different types of violation. + + Cross Dimension + +---------------------> + cross size range + |------------| + +--------+ +--------+ +--------+ +---------+ - - - - - - - - + Vertical |Vertical| |Vertical| |Vertical| |Vertical | | ^ + Stack 1 | Line 1 | | Line 2 | | Line 3 | | Line 4 | (zero violation) | stack size range + | | | | | | | | | | v + +--------+ +--------+ +--------+ +---------+ - - - - - - - - + | | + +--------+ +--------+ +--------+ - - - - - - - - - - - - + Vertical | | | | | | | | ^ + Stack 2 | | | | | |<--> (positive violation) | stack size range + | | | | | | | | v + +--------+ +--------+ +--------+ - - - - - - - - - - - - + | |<------> (negative violation) + +--------+ +--------+ +--------+ +---------+ +-----------+ - - - + Vertical | | | | | | | | | | | | ^ + Stack 3 | | | | | | | | | | | stack size range + | | | | | | | | | | | | v + +--------+ +--------+ +--------+ +---------+ +-----------+ - - - + + @param crossDimensionSum the consumed length of the lines in the stack along the cross dimension + @param style layout style to be applied to all children + @param sizeRange the range of allowable sizes for the stack layout spec + */ +CGFloat ASStackUnpositionedLayout::computeCrossViolation(const CGFloat crossDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); + const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); + if (crossDimensionSum < minCrossDimension) { + return minCrossDimension - crossDimensionSum; + } else if (crossDimensionSum > maxCrossDimension) { + return maxCrossDimension - crossDimensionSum; + } + return 0; +} /** Stretches children to lay out along the cross axis according to the alignment stretch settings of the children @@ -97,13 +167,13 @@ static const CGFloat kViolationEpsilon = 0.01; | +--------------------------------------------------+ + crossMax - @param items pre-computed child layouts; modified in-place as needed + @param items pre-computed items; modified in-place as needed @param style the layout style of the overall stack layout */ -static void stretchChildrenAlongCrossDimension(std::vector &items, - const ASStackLayoutSpecStyle &style, - const CGSize parentSize, - const CGFloat crossSize) +static void stretchItemsAlongCrossDimension(std::vector &items, + const ASStackLayoutSpecStyle &style, + const CGSize parentSize, + const CGFloat crossSize) { for (auto &item : items) { const ASStackLayoutAlignItems alignItems = alignment(item.child.style.alignSelf, style.alignItems); @@ -122,6 +192,33 @@ static void stretchChildrenAlongCrossDimension(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange, + const CGSize parentSize) +{ + ASDisplayNodeCAssertFalse(lines.empty()); + const std::size_t numOfLines = lines.size(); + const CGFloat violation = ASStackUnpositionedLayout::computeCrossViolation(computeLinesCrossDimensionSum(lines), style, sizeRange); + // Don't stretch if the stack is single line, because the line's cross size was clamped against the stack's constrained size. + const BOOL shouldStretchLines = (numOfLines > 1 + && style.alignContent == ASStackLayoutAlignContentStretch + && violation > kViolationEpsilon); + + CGFloat extraCrossSizePerLine = violation / numOfLines; + for (auto &line : lines) { + if (shouldStretchLines) { + line.crossSize += extraCrossSizePerLine; + } + + stretchItemsAlongCrossDimension(line.items, style, parentSize, line.crossSize); + } +} static BOOL itemIsBaselineAligned(const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecItem &l) @@ -131,20 +228,20 @@ static BOOL itemIsBaselineAligned(const ASStackLayoutSpecStyle &style, } CGFloat ASStackUnpositionedLayout::baselineForItem(const ASStackLayoutSpecStyle &style, - const ASStackLayoutSpecItem &l) + const ASStackLayoutSpecItem &item) { - switch (alignment(l.child.style.alignSelf, style.alignItems)) { + switch (alignment(item.child.style.alignSelf, style.alignItems)) { case ASStackLayoutAlignItemsBaselineFirst: - return l.child.style.ascender; + return item.child.style.ascender; case ASStackLayoutAlignItemsBaselineLast: - return crossDimension(style.direction, l.layout.size) + l.child.style.descender; + return crossDimension(style.direction, item.layout.size) + item.child.style.descender; default: return 0; } } /** - * Finds cross dimension size and baseline of the stack. + * Computes cross size and baseline of each line. * https://www.w3.org/TR/css-flexbox-1/#algo-cross-line * * @param items All items to lay out @@ -153,41 +250,64 @@ CGFloat ASStackUnpositionedLayout::baselineForItem(const ASStackLayoutSpecStyle * @param crossSize result of the cross size * @param baseline result of the stack baseline */ -static void computeCrossSizeAndBaseline(const std::vector &items, - const ASStackLayoutSpecStyle &style, - const ASSizeRange &sizeRange, - CGFloat &crossSize, - CGFloat &baseline) +static void computeLinesCrossSizeAndBaseline(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) { + ASDisplayNodeCAssertFalse(lines.empty()); + const BOOL isSingleLine = (lines.size() == 1); + const auto minCrossSize = crossDimension(style.direction, sizeRange.min); const auto maxCrossSize = crossDimension(style.direction, sizeRange.max); + const BOOL definiteCrossSize = (minCrossSize == maxCrossSize); - // Step 1. Collect all the flex items whose align-self is baseline. Find the largest of the distances - // between each item’s baseline and its hypothetical outer cross-start edge (aka. its ascender value), - // and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge - // (aka. the opposite of its descender value, because a negative descender means the item extends below its baseline), - // and sum these two values. - // - // Step 2. Find the maximum cross dimension size among child layouts. - CGFloat maxStartToBaselineDistance = 0; - CGFloat maxBaselineToEndDistance = 0; - CGFloat maxItemCrossSize = 0; - for (const auto &item : items) { - if (itemIsBaselineAligned(style, item)) { - CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item); - maxStartToBaselineDistance = MAX(maxStartToBaselineDistance, baseline); - maxBaselineToEndDistance = MAX(maxBaselineToEndDistance, crossDimension(style.direction, item.layout.size) - baseline); - } else { - maxItemCrossSize = MAX(maxItemCrossSize, crossDimension(style.direction, item.layout.size)); + // If the stack is single-line and has a definite cross size, the cross size of the line is the stack's definite cross size. + if (isSingleLine && definiteCrossSize) { + auto &line = lines[0]; + line.crossSize = minCrossSize; + + // We still need to determine the line's baseline + //TODO unit test + for (const auto &item : line.items) { + if (itemIsBaselineAligned(style, item)) { + CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item); + line.baseline = MAX(line.baseline, baseline); + } } + + return; } - // Step 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero. - crossSize = MAX(maxStartToBaselineDistance + maxBaselineToEndDistance, maxItemCrossSize);; - // Clamp the cross-size to be within the stack's min and max cross-size properties. - crossSize = MIN(MAX(minCrossSize, crossSize), maxCrossSize); - - baseline = maxStartToBaselineDistance; + for (auto &line : lines) { + const auto &items = line.items; + CGFloat maxStartToBaselineDistance = 0; + CGFloat maxBaselineToEndDistance = 0; + CGFloat maxItemCrossSize = 0; + + for (const auto &item : items) { + if (itemIsBaselineAligned(style, item)) { + // Step 1. Collect all the items whose align-self is baseline. Find the largest of the distances + // between each item’s baseline and its hypothetical outer cross-start edge (aka. its baseline value), + // and the largest of the distances between each item’s baseline and its hypothetical outer cross-end edge, + // and sum these two values. + CGFloat baseline = ASStackUnpositionedLayout::baselineForItem(style, item); + maxStartToBaselineDistance = MAX(maxStartToBaselineDistance, baseline); + maxBaselineToEndDistance = MAX(maxBaselineToEndDistance, crossDimension(style.direction, item.layout.size) - baseline); + } else { + // Step 2. Among all the items not collected by the previous step, find the largest outer hypothetical cross size. + maxItemCrossSize = MAX(maxItemCrossSize, crossDimension(style.direction, item.layout.size)); + } + } + + // Step 3. The used cross-size of the flex line is the largest of the numbers found in the previous two steps and zero. + line.crossSize = MAX(maxStartToBaselineDistance + maxBaselineToEndDistance, maxItemCrossSize); + if (isSingleLine) { + // If the stack is single-line, then clamp the line’s cross-size to be within the stack's min and max cross-size properties. + line.crossSize = MIN(MAX(minCrossSize, line.crossSize), maxCrossSize); + } + + line.baseline = maxStartToBaselineDistance; + } } /** @@ -314,8 +434,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector @param items unpositioned layouts for items @param style the layout style of the overall stack layout */ -static CGFloat computeStackDimensionSum(const std::vector &items, - const ASStackLayoutSpecStyle &style) +static CGFloat computeItemsStackDimensionSum(const std::vector &items, + const ASStackLayoutSpecStyle &style) { // Sum up the childrens' spacing const CGFloat childSpacingSum = std::accumulate(items.begin(), items.end(), @@ -333,6 +453,7 @@ static CGFloat computeStackDimensionSum(const std::vector return childStackDimensionSum; } +//TODO move this up near computeCrossViolation and make both methods share the same code path, to make sure they share the same concept of "negative" and "positive" violations. /** Computes the violation by comparing a stack dimension sum with the overall allowable size range for the stack. @@ -364,9 +485,9 @@ static CGFloat computeStackDimensionSum(const std::vector @param style layout style to be applied to all children @param sizeRange the range of allowable sizes for the stack layout spec */ -static CGFloat computeViolation(const CGFloat stackDimensionSum, - const ASStackLayoutSpecStyle &style, - const ASSizeRange &sizeRange) +CGFloat ASStackUnpositionedLayout::computeStackViolation(const CGFloat stackDimensionSum, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) { const CGFloat minStackDimension = stackDimension(style.direction, sizeRange.min); const CGFloat maxStackDimension = stackDimension(style.direction, sizeRange.max); @@ -394,65 +515,106 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector &items, - const ASStackLayoutSpecStyle &style, - const ASSizeRange &sizeRange, - const CGSize parentSize, - const BOOL useOptimizedFlexing) +static void flexLinesAlongStackDimension(std::vector &lines, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange, + const CGSize parentSize, + const BOOL useOptimizedFlexing) { - const CGFloat violation = computeViolation(computeStackDimensionSum(items, style), style, sizeRange); - std::function flexFactor = flexFactorInViolationDirection(violation); - // The flex factor sum is needed to determine if flexing is necessary. - // This value is also needed if the violation is positive and flexible children need to grow, so keep it around. - const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) { - return x + flexFactor(item); - }); - // If no children are able to flex then there is nothing left to do. Bail. - if (flexFactorSum == 0) { - // If optimized flexing was used then we have to clean up the unsized children and lay them out at zero size. - if (useOptimizedFlexing) { - layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, parentSize); + for (auto &line : lines) { + auto &items = line.items; + const CGFloat violation = ASStackUnpositionedLayout::computeStackViolation(computeItemsStackDimensionSum(items, style), style, sizeRange); + std::function flexFactor = flexFactorInViolationDirection(violation); + // The flex factor sum is needed to determine if flexing is necessary. + // This value is also needed if the violation is positive and flexible children need to grow, so keep it around. + const CGFloat flexFactorSum = std::accumulate(items.begin(), items.end(), 0.0, [&](CGFloat x, const ASStackLayoutSpecItem &item) { + return x + flexFactor(item); + }); + // If no children are able to flex then there is nothing left to do. Bail. + if (flexFactorSum == 0) { + // If optimized flexing was used then we have to clean up the unsized children and lay them out at zero size. + if (useOptimizedFlexing) { + layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, parentSize); + } + return; + } + std::function flexAdjustment = flexAdjustmentInViolationDirection(items, + style, + violation, + flexFactorSum); + + // Compute any remaining violation to the first flexible child. + const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) { + return x - flexAdjustment(item); + }); + BOOL isFirstFlex = YES; + for (ASStackLayoutSpecItem &item : items) { + const CGFloat currentFlexAdjustment = flexAdjustment(item); + // Children are consider inflexible if they do not need to make a flex adjustment. + if (currentFlexAdjustment != 0) { + const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size); + // Only apply the remaining violation for the first flexible child that has a flex grow factor. + const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0); + item.layout = crossChildLayout(item.child, + style, + MAX(flexedStackSize, 0), + MAX(flexedStackSize, 0), + crossDimension(style.direction, sizeRange.min), + crossDimension(style.direction, sizeRange.max), + parentSize); + isFirstFlex = NO; + } } - return; } - std::function flexAdjustment = flexAdjustmentInViolationDirection(items, - style, - violation, - flexFactorSum); +} - // Compute any remaining violation to the first flexible child. - const CGFloat remainingViolation = std::accumulate(items.begin(), items.end(), violation, [&](CGFloat x, const ASStackLayoutSpecItem &item) { - return x - flexAdjustment(item); - }); - BOOL isFirstFlex = YES; - for (ASStackLayoutSpecItem &item : items) { - const CGFloat currentFlexAdjustment = flexAdjustment(item); - // Children are consider inflexible if they do not need to make a flex adjustment. - if (currentFlexAdjustment != 0) { - const CGFloat originalStackSize = stackDimension(style.direction, item.layout.size); - // Only apply the remaining violation for the first flexible child that has a flex grow factor. - const CGFloat flexedStackSize = originalStackSize + currentFlexAdjustment + (isFirstFlex && item.child.style.flexGrow > 0 ? remainingViolation : 0); - item.layout = crossChildLayout(item.child, - style, - MAX(flexedStackSize, 0), - MAX(flexedStackSize, 0), - crossDimension(style.direction, sizeRange.min), - crossDimension(style.direction, sizeRange.max), - parentSize); - isFirstFlex = NO; - } +/** + https://www.w3.org/TR/css-flexbox-1/#algo-line-break + */ +static std::vector collectChildrenIntoLines(const std::vector &items, + const ASStackLayoutSpecStyle &style, + const ASSizeRange &sizeRange) +{ + //TODO if infinite max stack size, fast path + if (style.flexWrap == ASStackLayoutFlexWrapNoWrap) { + return std::vector (1, {.items = std::move(items)}); } + + std::vector lines; + std::vector lineItems; + CGFloat lineStackDimensionSum = 0; + + for(auto it = items.begin(); it != items.end(); ++it) { + const auto &item = *it; + const CGFloat itemStackDimension = stackDimension(style.direction, item.layout.size); + const BOOL negativeViolationIfAddItem = (ASStackUnpositionedLayout::computeStackViolation(lineStackDimensionSum + itemStackDimension, style, sizeRange) < 0); + const BOOL breakCurrentLine = negativeViolationIfAddItem && !lineItems.empty(); + + if (breakCurrentLine) { + lines.push_back({.items = std::vector (lineItems)}); + lineItems.clear(); + lineStackDimensionSum = 0; + } + + lineItems.push_back(std::move(item)); + lineStackDimensionSum += itemStackDimension; + } + + // Handle last line + lines.push_back({.items = std::vector (lineItems)}); + + return lines; } /** @@ -507,25 +669,34 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector items = layoutChildrenAlongUnconstrainedStackDimension(children, - style, - sizeRange, - parentSize, - optimizedFlexing); + style, + sizeRange, + parentSize, + optimizedFlexing); - // Resolve the flexible lengths (https://www.w3.org/TR/css-flexbox-1/#algo-flex) - // Determine the hypothetical cross size of each item (https://www.w3.org/TR/css-flexbox-1/#algo-cross-item) - flexChildrenAlongStackDimension(items, style, sizeRange, parentSize, optimizedFlexing); + // Collect items into lines (https://www.w3.org/TR/css-flexbox-1/#algo-line-break) + std::vector lines = collectChildrenIntoLines(items, style, sizeRange); - // Step 4. Cross Size Determination (https://www.w3.org/TR/css-flexbox-1/#cross-sizing) - // - // Calculate the cross size of the stack (https://www.w3.org/TR/css-flexbox-1/#algo-cross-line) - CGFloat crossSize; - CGFloat baseline; - computeCrossSizeAndBaseline(items, style, sizeRange, crossSize, baseline); + // Resolve the flexible lengths (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) + flexLinesAlongStackDimension(lines, style, sizeRange, parentSize, optimizedFlexing); + + // Calculate the cross size of each flex line (https://www.w3.org/TR/css-flexbox-1/#algo-cross-line) + computeLinesCrossSizeAndBaseline(lines, style, sizeRange); + + // Handle 'align-content: stretch' (https://www.w3.org/TR/css-flexbox-1/#algo-line-stretch) // Determine the used cross size of each item (https://www.w3.org/TR/css-flexbox-1/#algo-stretch) - // If the flex item has stretch alignment, redo layout - stretchChildrenAlongCrossDimension(items, style, parentSize, crossSize); + stretchLinesAlongCrossDimension(lines, style, sizeRange, parentSize); - const CGFloat stackDimensionSum = computeStackDimensionSum(items, style); - return {std::move(items), stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange), crossSize, baseline}; + // Compute stack dimension sum of each line and the whole stack + CGFloat layoutStackDimensionSum = 0; + for (auto &line : lines) { + line.stackDimensionSum = computeItemsStackDimensionSum(line.items, style); + // layoutStackDimensionSum is the max stackDimensionSum among all lines + layoutStackDimensionSum = MAX(line.stackDimensionSum, layoutStackDimensionSum); + } + // Compute cross dimension sum of the stack. + // This should be done before `lines` are moved to a new ASStackUnpositionedLayout struct (i.e `std::move(lines)`) + CGFloat layoutCrossDimensionSum = computeLinesCrossDimensionSum(lines); + + return {.lines = std::move(lines), .stackDimensionSum = layoutStackDimensionSum, .crossDimensionSum = layoutCrossDimensionSum}; } diff --git a/AsyncDisplayKit/Private/_ASCoreAnimationExtras.h b/Source/Private/_ASCoreAnimationExtras.h similarity index 100% rename from AsyncDisplayKit/Private/_ASCoreAnimationExtras.h rename to Source/Private/_ASCoreAnimationExtras.h diff --git a/AsyncDisplayKit/Private/_ASCoreAnimationExtras.mm b/Source/Private/_ASCoreAnimationExtras.mm similarity index 100% rename from AsyncDisplayKit/Private/_ASCoreAnimationExtras.mm rename to Source/Private/_ASCoreAnimationExtras.mm diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h b/Source/Private/_ASHierarchyChangeSet.h similarity index 94% rename from AsyncDisplayKit/Private/_ASHierarchyChangeSet.h rename to Source/Private/_ASHierarchyChangeSet.h index 9c00625259..1e545d0abd 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.h +++ b/Source/Private/_ASHierarchyChangeSet.h @@ -95,15 +95,6 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); - (instancetype)initWithOldData:(std::vector)oldItemCounts NS_DESIGNATED_INITIALIZER; - -/** - * The combined completion handler. - * - * @warning The completion block is discarded after reading because it may have captured - * significant resources that we would like to reclaim as soon as possible. - */ -@property (nonatomic, copy, readonly, nullable) void(^completionHandler)(BOOL finished); - /** * Append the given completion handler to the combined @c completionHandler. * @@ -114,6 +105,14 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); */ - (void)addCompletionHandler:(nullable void(^)(BOOL finished))completion; +/** + * Execute the combined completion handler. + * + * @warning The completion block is discarded after reading because it may have captured + * significant resources that we would like to reclaim as soon as possible. + */ +- (void)executeCompletionHandlerWithFinished:(BOOL)finished; + /// @precondition The change set must be completed. @property (nonatomic, strong, readonly) NSIndexSet *deletedSections; /// @precondition The change set must be completed. @@ -128,6 +127,10 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); - (NSUInteger)newSectionForOldSection:(NSUInteger)oldSection; @property (nonatomic, readonly) BOOL completed; +/// Whether or not changes should be animated. +// TODO: if any update in this chagne set is non-animated, the whole update should be non-animated. +@property (nonatomic, readwrite) BOOL animated; +@property (nonatomic, readonly) BOOL includesReloadData; /// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error. /// NOTE: Calling this method will cause the changeset to convert all reloads into delete/insert pairs. @@ -139,6 +142,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType); /// Returns all item indexes affected by changes of the given type in the given section. - (NSIndexSet *)indexesForItemChangesOfType:(_ASHierarchyChangeType)changeType inSection:(NSUInteger)section; +- (void)reloadData; - (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; - (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; - (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options; diff --git a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.mm b/Source/Private/_ASHierarchyChangeSet.mm similarity index 97% rename from AsyncDisplayKit/Private/_ASHierarchyChangeSet.mm rename to Source/Private/_ASHierarchyChangeSet.mm index 82814af04a..411b9c8c10 100644 --- a/AsyncDisplayKit/Private/_ASHierarchyChangeSet.mm +++ b/Source/Private/_ASHierarchyChangeSet.mm @@ -149,13 +149,6 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) #pragma mark External API -- (void (^)(BOOL finished))completionHandler -{ - void (^completionHandler)(BOOL) = _completionHandler; - _completionHandler = nil; - return completionHandler; -} - - (void)addCompletionHandler:(void (^)(BOOL))completion { [self _ensureNotCompleted]; @@ -172,6 +165,14 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) }; } +- (void)executeCompletionHandlerWithFinished:(BOOL)finished +{ + if (_completionHandler != nil) { + _completionHandler(finished); + _completionHandler = nil; + } +} + - (void)markCompletedWithNewItemCounts:(std::vector)newItemCounts { NSAssert(!_completed, @"Attempt to mark already-completed changeset as completed."); @@ -245,6 +246,13 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) return newIndex; } +- (void)reloadData +{ + [self _ensureNotCompleted]; + NSAssert(_includesReloadData == NO, @"Attempt to reload data multiple times %@", self); + _includesReloadData = YES; +} + - (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options { [self _ensureNotCompleted]; @@ -321,6 +329,10 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (void)_sortAndCoalesceChangeArrays { + if (_includesReloadData) { + return; + } + @autoreleasepool { // Split reloaded sections into [delete(oldIndex), insert(newIndex)] @@ -411,6 +423,17 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (void)_validateUpdate { + // Assert that if reloadData exists, it's the only change + // TODO: remove this and be lenient on them? + if (_includesReloadData) { + if (0 < (_originalDeleteSectionChanges.count + _originalDeleteItemChanges.count + +_originalInsertSectionChanges.count + _originalInsertItemChanges.count + + _reloadSectionChanges.count + _reloadItemChanges.count)) { + ASFailUpdateValidation(@"Attempt to reload data in conjuntion with other updates."); + } + return; + } + NSIndexSet *allReloadedSections = [_ASHierarchySectionChange allIndexesInSectionChanges:_reloadSectionChanges]; NSInteger newSectionCount = _newItemCounts.size(); @@ -536,6 +559,7 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType) - (NSMutableArray *)propertiesForDescription { NSMutableArray *result = [NSMutableArray array]; + [result addObject:@{ @"includesReloadData" : @(_includesReloadData) }]; if (_reloadSectionChanges.count > 0) { [result addObject:@{ @"reloadSections" : [_ASHierarchySectionChange smallDescriptionForSectionChanges:_reloadSectionChanges] }]; } diff --git a/AsyncDisplayKit/Private/_ASPendingState.h b/Source/Private/_ASPendingState.h similarity index 100% rename from AsyncDisplayKit/Private/_ASPendingState.h rename to Source/Private/_ASPendingState.h diff --git a/AsyncDisplayKit/Private/_ASPendingState.mm b/Source/Private/_ASPendingState.mm similarity index 100% rename from AsyncDisplayKit/Private/_ASPendingState.mm rename to Source/Private/_ASPendingState.mm diff --git a/AsyncDisplayKit/Private/_ASScopeTimer.h b/Source/Private/_ASScopeTimer.h similarity index 100% rename from AsyncDisplayKit/Private/_ASScopeTimer.h rename to Source/Private/_ASScopeTimer.h diff --git a/AsyncDisplayKit/TextKit/ASEqualityHashHelpers.h b/Source/TextKit/ASEqualityHashHelpers.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASEqualityHashHelpers.h rename to Source/TextKit/ASEqualityHashHelpers.h diff --git a/AsyncDisplayKit/TextKit/ASEqualityHashHelpers.mm b/Source/TextKit/ASEqualityHashHelpers.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASEqualityHashHelpers.mm rename to Source/TextKit/ASEqualityHashHelpers.mm diff --git a/AsyncDisplayKit/TextKit/ASLayoutManager.h b/Source/TextKit/ASLayoutManager.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASLayoutManager.h rename to Source/TextKit/ASLayoutManager.h diff --git a/AsyncDisplayKit/TextKit/ASLayoutManager.m b/Source/TextKit/ASLayoutManager.m similarity index 100% rename from AsyncDisplayKit/TextKit/ASLayoutManager.m rename to Source/TextKit/ASLayoutManager.m diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.h b/Source/TextKit/ASTextKitAttributes.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitAttributes.h rename to Source/TextKit/ASTextKitAttributes.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitAttributes.mm b/Source/TextKit/ASTextKitAttributes.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitAttributes.mm rename to Source/TextKit/ASTextKitAttributes.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitComponents.h b/Source/TextKit/ASTextKitComponents.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitComponents.h rename to Source/TextKit/ASTextKitComponents.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitComponents.mm b/Source/TextKit/ASTextKitComponents.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitComponents.mm rename to Source/TextKit/ASTextKitComponents.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.h b/Source/TextKit/ASTextKitContext.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitContext.h rename to Source/TextKit/ASTextKitContext.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitContext.mm b/Source/TextKit/ASTextKitContext.mm similarity index 86% rename from AsyncDisplayKit/TextKit/ASTextKitContext.mm rename to Source/TextKit/ASTextKitContext.mm index 480b3fd46a..79f40c5813 100755 --- a/AsyncDisplayKit/TextKit/ASTextKitContext.mm +++ b/Source/TextKit/ASTextKitContext.mm @@ -39,10 +39,18 @@ __instanceLock__ = std::make_shared(); // Create the TextKit component stack with our default configuration. - _textStorage = (attributedString ? [[NSTextStorage alloc] initWithAttributedString:attributedString] : [[NSTextStorage alloc] init]); + + _textStorage = [[NSTextStorage alloc] init]; _layoutManager = [[ASLayoutManager alloc] init]; _layoutManager.usesFontLeading = NO; [_textStorage addLayoutManager:_layoutManager]; + + // Instead of calling [NSTextStorage initWithAttributedString:], setting attributedString just after calling addlayoutManager can fix CJK language layout issues. + // See https://github.com/facebook/AsyncDisplayKit/issues/2894 + if (attributedString) { + [_textStorage setAttributedString:attributedString]; + } + _textContainer = [[NSTextContainer alloc] initWithSize:constrainedSize]; // We want the text laid out up to the very edges of the container. _textContainer.lineFragmentPadding = 0; diff --git a/AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.h b/Source/TextKit/ASTextKitCoreTextAdditions.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.h rename to Source/TextKit/ASTextKitCoreTextAdditions.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.m b/Source/TextKit/ASTextKitCoreTextAdditions.m similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitCoreTextAdditions.m rename to Source/TextKit/ASTextKitCoreTextAdditions.m diff --git a/AsyncDisplayKit/TextKit/ASTextKitEntityAttribute.h b/Source/TextKit/ASTextKitEntityAttribute.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitEntityAttribute.h rename to Source/TextKit/ASTextKitEntityAttribute.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitEntityAttribute.m b/Source/TextKit/ASTextKitEntityAttribute.m similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitEntityAttribute.m rename to Source/TextKit/ASTextKitEntityAttribute.m diff --git a/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.h b/Source/TextKit/ASTextKitFontSizeAdjuster.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.h rename to Source/TextKit/ASTextKitFontSizeAdjuster.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm b/Source/TextKit/ASTextKitFontSizeAdjuster.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitFontSizeAdjuster.mm rename to Source/TextKit/ASTextKitFontSizeAdjuster.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer+Positioning.h b/Source/TextKit/ASTextKitRenderer+Positioning.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer+Positioning.h rename to Source/TextKit/ASTextKitRenderer+Positioning.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer+Positioning.mm b/Source/TextKit/ASTextKitRenderer+Positioning.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer+Positioning.mm rename to Source/TextKit/ASTextKitRenderer+Positioning.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer+TextChecking.h b/Source/TextKit/ASTextKitRenderer+TextChecking.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer+TextChecking.h rename to Source/TextKit/ASTextKitRenderer+TextChecking.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer+TextChecking.mm b/Source/TextKit/ASTextKitRenderer+TextChecking.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer+TextChecking.mm rename to Source/TextKit/ASTextKitRenderer+TextChecking.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.h b/Source/TextKit/ASTextKitRenderer.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer.h rename to Source/TextKit/ASTextKitRenderer.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitRenderer.mm b/Source/TextKit/ASTextKitRenderer.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitRenderer.mm rename to Source/TextKit/ASTextKitRenderer.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitShadower.h b/Source/TextKit/ASTextKitShadower.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitShadower.h rename to Source/TextKit/ASTextKitShadower.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitShadower.mm b/Source/TextKit/ASTextKitShadower.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitShadower.mm rename to Source/TextKit/ASTextKitShadower.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.h b/Source/TextKit/ASTextKitTailTruncater.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitTailTruncater.h rename to Source/TextKit/ASTextKitTailTruncater.h diff --git a/AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm b/Source/TextKit/ASTextKitTailTruncater.mm similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitTailTruncater.mm rename to Source/TextKit/ASTextKitTailTruncater.mm diff --git a/AsyncDisplayKit/TextKit/ASTextKitTruncating.h b/Source/TextKit/ASTextKitTruncating.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextKitTruncating.h rename to Source/TextKit/ASTextKitTruncating.h diff --git a/AsyncDisplayKit/TextKit/ASTextNodeTypes.h b/Source/TextKit/ASTextNodeTypes.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextNodeTypes.h rename to Source/TextKit/ASTextNodeTypes.h diff --git a/AsyncDisplayKit/TextKit/ASTextNodeWordKerner.h b/Source/TextKit/ASTextNodeWordKerner.h similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextNodeWordKerner.h rename to Source/TextKit/ASTextNodeWordKerner.h diff --git a/AsyncDisplayKit/TextKit/ASTextNodeWordKerner.m b/Source/TextKit/ASTextNodeWordKerner.m similarity index 100% rename from AsyncDisplayKit/TextKit/ASTextNodeWordKerner.m rename to Source/TextKit/ASTextNodeWordKerner.m diff --git a/AsyncDisplayKit/UIImage+ASConvenience.h b/Source/UIImage+ASConvenience.h similarity index 100% rename from AsyncDisplayKit/UIImage+ASConvenience.h rename to Source/UIImage+ASConvenience.h diff --git a/AsyncDisplayKit/UIImage+ASConvenience.m b/Source/UIImage+ASConvenience.m similarity index 100% rename from AsyncDisplayKit/UIImage+ASConvenience.m rename to Source/UIImage+ASConvenience.m diff --git a/Source/UIResponder+AsyncDisplayKit.h b/Source/UIResponder+AsyncDisplayKit.h new file mode 100644 index 0000000000..c39b5275a9 --- /dev/null +++ b/Source/UIResponder+AsyncDisplayKit.h @@ -0,0 +1,24 @@ +// +// UIResponder+AsyncDisplayKit.h +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/13/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIResponder (AsyncDisplayKit) + +/** + * The nearest view controller above this responder, if one exists. + * + * This property must be accessed on the main thread. + */ +@property (nonatomic, nullable, readonly) __kindof UIViewController *asdk_associatedViewController; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/UIResponder+AsyncDisplayKit.m b/Source/UIResponder+AsyncDisplayKit.m new file mode 100644 index 0000000000..55e4c9fbfa --- /dev/null +++ b/Source/UIResponder+AsyncDisplayKit.m @@ -0,0 +1,31 @@ +// +// UIResponder+AsyncDisplayKit.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/13/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import "UIResponder+AsyncDisplayKit.h" + +#import +#import +#import + +@implementation UIResponder (AsyncDisplayKit) + +- (__kindof UIViewController *)asdk_associatedViewController +{ + ASDisplayNodeAssertMainThread(); + + for (UIResponder *responder in [self asdk_responderChainEnumerator]) { + UIViewController *vc = ASDynamicCast(responder, UIViewController); + if (vc) { + return vc; + } + } + return nil; +} + +@end + diff --git a/AsyncDisplayKit/_ASTransitionContext.h b/Source/_ASTransitionContext.h similarity index 100% rename from AsyncDisplayKit/_ASTransitionContext.h rename to Source/_ASTransitionContext.h diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/Source/_ASTransitionContext.m similarity index 88% rename from AsyncDisplayKit/_ASTransitionContext.m rename to Source/_ASTransitionContext.m index 695fba8c1d..fac52d28e0 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/Source/_ASTransitionContext.m @@ -54,22 +54,12 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi - (CGRect)initialFrameForNode:(ASDisplayNode *)node { - for (ASDisplayNode *subnode in [_layoutDelegate currentSubnodesWithTransitionContext:self]) { - if (node == subnode) { - return node.frame; - } - } - return CGRectZero; + return [[self layoutForKey:ASTransitionContextFromLayoutKey] frameForElement:node]; } - (CGRect)finalFrameForNode:(ASDisplayNode *)node { - for (ASLayout *layout in [self layoutForKey:ASTransitionContextToLayoutKey].sublayouts) { - if (layout.layoutElement == node) { - return [layout frame]; - } - } - return CGRectZero; + return [[self layoutForKey:ASTransitionContextToLayoutKey] frameForElement:node]; } - (NSArray *)subnodesForKey:(NSString *)key diff --git a/Source/module.modulemap b/Source/module.modulemap new file mode 100644 index 0000000000..fd7d49e620 --- /dev/null +++ b/Source/module.modulemap @@ -0,0 +1,19 @@ +framework module AsyncDisplayKit { + umbrella header "AsyncDisplayKit.h" + + export * + module * { + export * + } + + explicit module ASControlNode_Subclasses { + header "ASControlNode+Subclasses.h" + export * + } + + explicit module ASDisplayNode_Subclasses { + header "ASDisplayNode+Subclasses.h" + export * + } + +} diff --git a/AsyncDisplayKit/tvOS/ASControlNode+tvOS.h b/Source/tvOS/ASControlNode+tvOS.h similarity index 100% rename from AsyncDisplayKit/tvOS/ASControlNode+tvOS.h rename to Source/tvOS/ASControlNode+tvOS.h diff --git a/AsyncDisplayKit/tvOS/ASControlNode+tvOS.m b/Source/tvOS/ASControlNode+tvOS.m similarity index 100% rename from AsyncDisplayKit/tvOS/ASControlNode+tvOS.m rename to Source/tvOS/ASControlNode+tvOS.m diff --git a/AsyncDisplayKit/tvOS/ASImageNode+tvOS.h b/Source/tvOS/ASImageNode+tvOS.h similarity index 100% rename from AsyncDisplayKit/tvOS/ASImageNode+tvOS.h rename to Source/tvOS/ASImageNode+tvOS.h diff --git a/AsyncDisplayKit/tvOS/ASImageNode+tvOS.m b/Source/tvOS/ASImageNode+tvOS.m similarity index 100% rename from AsyncDisplayKit/tvOS/ASImageNode+tvOS.m rename to Source/tvOS/ASImageNode+tvOS.m diff --git a/AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m b/Tests/ASAbsoluteLayoutSpecSnapshotTests.m similarity index 100% rename from AsyncDisplayKitTests/ASAbsoluteLayoutSpecSnapshotTests.m rename to Tests/ASAbsoluteLayoutSpecSnapshotTests.m diff --git a/AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm b/Tests/ASBackgroundLayoutSpecSnapshotTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm rename to Tests/ASBackgroundLayoutSpecSnapshotTests.mm diff --git a/AsyncDisplayKitTests/ASBasicImageDownloaderContextTests.m b/Tests/ASBasicImageDownloaderContextTests.m similarity index 100% rename from AsyncDisplayKitTests/ASBasicImageDownloaderContextTests.m rename to Tests/ASBasicImageDownloaderContextTests.m diff --git a/AsyncDisplayKitTests/ASBasicImageDownloaderTests.m b/Tests/ASBasicImageDownloaderTests.m similarity index 100% rename from AsyncDisplayKitTests/ASBasicImageDownloaderTests.m rename to Tests/ASBasicImageDownloaderTests.m diff --git a/AsyncDisplayKitTests/ASBatchFetchingTests.m b/Tests/ASBatchFetchingTests.m similarity index 100% rename from AsyncDisplayKitTests/ASBatchFetchingTests.m rename to Tests/ASBatchFetchingTests.m diff --git a/AsyncDisplayKitTests/ASBridgedPropertiesTests.mm b/Tests/ASBridgedPropertiesTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASBridgedPropertiesTests.mm rename to Tests/ASBridgedPropertiesTests.mm diff --git a/AsyncDisplayKitTests/ASCALayerTests.m b/Tests/ASCALayerTests.m similarity index 100% rename from AsyncDisplayKitTests/ASCALayerTests.m rename to Tests/ASCALayerTests.m diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/Tests/ASCenterLayoutSpecSnapshotTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm rename to Tests/ASCenterLayoutSpecSnapshotTests.mm diff --git a/AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m b/Tests/ASCollectionViewFlowLayoutInspectorTests.m similarity index 100% rename from AsyncDisplayKitTests/ASCollectionViewFlowLayoutInspectorTests.m rename to Tests/ASCollectionViewFlowLayoutInspectorTests.m diff --git a/AsyncDisplayKitTests/ASCollectionViewTests.mm b/Tests/ASCollectionViewTests.mm similarity index 99% rename from AsyncDisplayKitTests/ASCollectionViewTests.mm rename to Tests/ASCollectionViewTests.mm index 6628770621..af96805f89 100644 --- a/AsyncDisplayKitTests/ASCollectionViewTests.mm +++ b/Tests/ASCollectionViewTests.mm @@ -10,8 +10,8 @@ #import #import -#import #import +#import #import #import #import @@ -161,7 +161,7 @@ @interface ASCollectionView (InternalTesting) -- (NSArray *)supplementaryNodeKindsInDataController:(ASCollectionDataController *)dataController sections:(nonnull NSIndexSet *)sections; +- (NSArray *)dataController:(ASDataController *)dataController supplementaryNodeKindsInSections:(NSIndexSet *)sections; @end @@ -216,7 +216,7 @@ UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; ASCollectionView *collectionView = [[ASCollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout]; [collectionView registerSupplementaryNodeOfKind:UICollectionElementKindSectionHeader]; - XCTAssertEqualObjects([collectionView supplementaryNodeKindsInDataController:nil sections:[NSIndexSet indexSetWithIndex:0]], @[UICollectionElementKindSectionHeader]); + XCTAssertEqualObjects([collectionView dataController:nil supplementaryNodeKindsInSections:[NSIndexSet indexSetWithIndex:0]], @[UICollectionElementKindSectionHeader]); } - (void)testReloadIfNeeded @@ -985,8 +985,10 @@ [window makeKeyAndVisible]; [view layoutIfNeeded]; - + + [cn waitUntilAllUpdatesAreCommitted]; [self waitForExpectationsWithTimeout:60 handler:nil]; + CGFloat contentHeight = cn.view.contentSize.height; CGFloat requiredContentHeight; CGFloat itemHeight = [cn.view layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]].size.height; @@ -1029,7 +1031,7 @@ r; }); CGFloat expectedHeight = cn.bounds.size.height * 3; - XCTAssertEqualWithAccuracy(CGRectGetHeight(preloadBounds), expectedHeight, 100); + XCTAssertEqualWithAccuracy(CGRectGetHeight(preloadBounds), expectedHeight, expectedHeight * 0.1); XCTAssertEqual([[cn valueForKeyPath:@"rangeController.currentRangeMode"] integerValue], ASLayoutRangeModeMinimum, @"Expected range mode to be minimum before scrolling begins."); } diff --git a/AsyncDisplayKitTests/ASControlNodeTests.m b/Tests/ASControlNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASControlNodeTests.m rename to Tests/ASControlNodeTests.m diff --git a/AsyncDisplayKitTests/ASDimensionTests.mm b/Tests/ASDimensionTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASDimensionTests.mm rename to Tests/ASDimensionTests.mm diff --git a/AsyncDisplayKitTests/ASDispatchTests.m b/Tests/ASDispatchTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDispatchTests.m rename to Tests/ASDispatchTests.m diff --git a/AsyncDisplayKitTests/ASDisplayLayerTests.m b/Tests/ASDisplayLayerTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayLayerTests.m rename to Tests/ASDisplayLayerTests.m diff --git a/AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m b/Tests/ASDisplayNodeAppearanceTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeAppearanceTests.m rename to Tests/ASDisplayNodeAppearanceTests.m diff --git a/AsyncDisplayKitTests/ASDisplayNodeExtrasTests.m b/Tests/ASDisplayNodeExtrasTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeExtrasTests.m rename to Tests/ASDisplayNodeExtrasTests.m diff --git a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m b/Tests/ASDisplayNodeImplicitHierarchyTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m rename to Tests/ASDisplayNodeImplicitHierarchyTests.m diff --git a/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm b/Tests/ASDisplayNodeLayoutTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm rename to Tests/ASDisplayNodeLayoutTests.mm diff --git a/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m b/Tests/ASDisplayNodeSnapshotTests.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m rename to Tests/ASDisplayNodeSnapshotTests.m diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/Tests/ASDisplayNodeTests.mm similarity index 99% rename from AsyncDisplayKitTests/ASDisplayNodeTests.m rename to Tests/ASDisplayNodeTests.mm index e5c7ebe7a6..39c2d68486 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/Tests/ASDisplayNodeTests.mm @@ -18,6 +18,7 @@ #import #import #import +#import #import "ASDisplayNodeTestsHelper.h" #import #import @@ -1102,33 +1103,34 @@ static inline BOOL _CGPointEqualToPointWithEpsilon(CGPoint point1, CGPoint point - (void)testReplaceSubnodeNoView { - [self checkReplaceSubnodeWithView:NO layerBacked:NO]; + [self checkReplaceSubnodeLoaded:NO layerBacked:NO]; } - (void)testReplaceSubnodeNoLayer { - [self checkReplaceSubnodeWithView:NO layerBacked:YES]; + [self checkReplaceSubnodeLoaded:NO layerBacked:YES]; } - (void)testReplaceSubnodeView { - [self checkReplaceSubnodeWithView:YES layerBacked:NO]; + [self checkReplaceSubnodeLoaded:YES layerBacked:NO]; } - (void)testReplaceSubnodeLayer { - [self checkReplaceSubnodeWithView:YES layerBacked:YES]; + [self checkReplaceSubnodeLoaded:YES layerBacked:YES]; } -- (void)checkReplaceSubnodeWithView:(BOOL)loaded layerBacked:(BOOL)isLayerBacked +- (void)checkReplaceSubnodeLoaded:(BOOL)loaded layerBacked:(BOOL)isLayerBacked { DeclareNodeNamed(parent); DeclareNodeNamed(a); DeclareNodeNamed(b); DeclareNodeNamed(c); + DeclareNodeNamed(d); - for (ASDisplayNode *n in @[parent, a, b, c]) { + for (ASDisplayNode *n in @[parent, a, b, c, d]) { n.layerBacked = isLayerBacked; } @@ -1140,7 +1142,6 @@ static inline BOOL _CGPointEqualToPointWithEpsilon(CGPoint point1, CGPoint point [parent layer]; } - DeclareNodeNamed(d); if (loaded) { XCTAssertFalse(d.nodeLoaded, @"Should not yet be loaded"); } @@ -2153,12 +2154,9 @@ static bool stringContainsPointer(NSString *description, id p) { { ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init]; [node view]; - dispatch_semaphore_t sem = dispatch_semaphore_create(0); - [NSThread detachNewThreadWithBlock:^{ + [self executeOffThread:^{ XCTAssertThrows([node onDidLoad:^(ASDisplayNode * _Nonnull node) { }]); - dispatch_semaphore_signal(sem); }]; - dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); } - (void)testThatOnDidLoadWorks diff --git a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h b/Tests/ASDisplayNodeTestsHelper.h similarity index 92% rename from AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h rename to Tests/ASDisplayNodeTestsHelper.h index 618d82df94..21a149e4b2 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.h +++ b/Tests/ASDisplayNodeTestsHelper.h @@ -15,7 +15,11 @@ typedef BOOL (^as_condition_block_t)(void); +ASDISPLAYNODE_EXTERN_C_BEGIN + BOOL ASDisplayNodeRunRunLoopUntilBlockIsTrue(as_condition_block_t block); void ASDisplayNodeSizeToFitSize(ASDisplayNode *node, CGSize size); void ASDisplayNodeSizeToFitSizeRange(ASDisplayNode *node, ASSizeRange sizeRange); + +ASDISPLAYNODE_EXTERN_C_END diff --git a/AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m b/Tests/ASDisplayNodeTestsHelper.m similarity index 100% rename from AsyncDisplayKitTests/ASDisplayNodeTestsHelper.m rename to Tests/ASDisplayNodeTestsHelper.m diff --git a/AsyncDisplayKitTests/ASEditableTextNodeTests.m b/Tests/ASEditableTextNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASEditableTextNodeTests.m rename to Tests/ASEditableTextNodeTests.m diff --git a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m b/Tests/ASImageNodeSnapshotTests.m similarity index 97% rename from AsyncDisplayKitTests/ASImageNodeSnapshotTests.m rename to Tests/ASImageNodeSnapshotTests.m index 9ebb36ce30..bb9c730d3d 100644 --- a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m +++ b/Tests/ASImageNodeSnapshotTests.m @@ -17,6 +17,13 @@ @implementation ASImageNodeSnapshotTests +- (void)setUp +{ + [super setUp]; + + self.recordMode = NO; +} + - (UIImage *)testImage { NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square" diff --git a/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm b/Tests/ASInsetLayoutSpecSnapshotTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm rename to Tests/ASInsetLayoutSpecSnapshotTests.mm diff --git a/AsyncDisplayKitTests/ASLayoutElementStyleTests.m b/Tests/ASLayoutElementStyleTests.m similarity index 100% rename from AsyncDisplayKitTests/ASLayoutElementStyleTests.m rename to Tests/ASLayoutElementStyleTests.m diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h b/Tests/ASLayoutSpecSnapshotTestsHelper.h similarity index 100% rename from AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h rename to Tests/ASLayoutSpecSnapshotTestsHelper.h diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/Tests/ASLayoutSpecSnapshotTestsHelper.m similarity index 100% rename from AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m rename to Tests/ASLayoutSpecSnapshotTestsHelper.m diff --git a/AsyncDisplayKitTests/ASLayoutSpecTests.m b/Tests/ASLayoutSpecTests.m similarity index 100% rename from AsyncDisplayKitTests/ASLayoutSpecTests.m rename to Tests/ASLayoutSpecTests.m diff --git a/AsyncDisplayKitTests/ASMultiplexImageNodeTests.m b/Tests/ASMultiplexImageNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASMultiplexImageNodeTests.m rename to Tests/ASMultiplexImageNodeTests.m diff --git a/AsyncDisplayKitTests/ASMutableAttributedStringBuilderTests.m b/Tests/ASMutableAttributedStringBuilderTests.m similarity index 100% rename from AsyncDisplayKitTests/ASMutableAttributedStringBuilderTests.m rename to Tests/ASMutableAttributedStringBuilderTests.m diff --git a/AsyncDisplayKitTests/ASNetworkImageNodeTests.m b/Tests/ASNetworkImageNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASNetworkImageNodeTests.m rename to Tests/ASNetworkImageNodeTests.m diff --git a/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm b/Tests/ASOverlayLayoutSpecSnapshotTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm rename to Tests/ASOverlayLayoutSpecSnapshotTests.mm diff --git a/AsyncDisplayKitTests/ASPagerNodeTests.m b/Tests/ASPagerNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASPagerNodeTests.m rename to Tests/ASPagerNodeTests.m diff --git a/AsyncDisplayKitTests/ASPerformanceTestContext.h b/Tests/ASPerformanceTestContext.h similarity index 100% rename from AsyncDisplayKitTests/ASPerformanceTestContext.h rename to Tests/ASPerformanceTestContext.h diff --git a/AsyncDisplayKitTests/ASPerformanceTestContext.m b/Tests/ASPerformanceTestContext.m similarity index 100% rename from AsyncDisplayKitTests/ASPerformanceTestContext.m rename to Tests/ASPerformanceTestContext.m diff --git a/AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m b/Tests/ASPhotosFrameworkImageRequestTests.m similarity index 100% rename from AsyncDisplayKitTests/ASPhotosFrameworkImageRequestTests.m rename to Tests/ASPhotosFrameworkImageRequestTests.m diff --git a/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm b/Tests/ASRatioLayoutSpecSnapshotTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm rename to Tests/ASRatioLayoutSpecSnapshotTests.mm diff --git a/Tests/ASRectTableTests.m b/Tests/ASRectTableTests.m new file mode 100644 index 0000000000..55c1dbab15 --- /dev/null +++ b/Tests/ASRectTableTests.m @@ -0,0 +1,51 @@ +// +// ASRectTableTests.m +// AsyncDisplayKit +// +// Created by Adlai Holler on 2/24/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +#import "ASRectTable.h" +#import "ASXCTExtensions.h" + +@interface ASRectTableTests : XCTestCase +@end + +@implementation ASRectTableTests + +- (void)testThatItStoresRects +{ + ASRectTable *table = [ASRectTable rectTableForWeakObjectPointers]; + NSObject *key0 = [[NSObject alloc] init]; + NSObject *key1 = [[NSObject alloc] init]; + ASXCTAssertEqualRects([table rectForKey:key0], CGRectNull); + ASXCTAssertEqualRects([table rectForKey:key1], CGRectNull); + CGRect rect0 = CGRectMake(0, 0, 100, 100); + CGRect rect1 = CGRectMake(0, 0, 50, 50); + [table setRect:rect0 forKey:key0]; + [table setRect:rect1 forKey:key1]; + + ASXCTAssertEqualRects([table rectForKey:key0], rect0); + ASXCTAssertEqualRects([table rectForKey:key1], rect1); +} + + +- (void)testCopying +{ + ASRectTable *table = [ASRectTable rectTableForWeakObjectPointers]; + NSObject *key = [[NSObject alloc] init]; + ASXCTAssertEqualRects([table rectForKey:key], CGRectNull); + CGRect rect0 = CGRectMake(0, 0, 100, 100); + CGRect rect1 = CGRectMake(0, 0, 50, 50); + [table setRect:rect0 forKey:key]; + ASRectTable *copy = [table copy]; + [copy setRect:rect1 forKey:key]; + + ASXCTAssertEqualRects([table rectForKey:key], rect0); + ASXCTAssertEqualRects([copy rectForKey:key], rect1); +} + +@end diff --git a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm b/Tests/ASRelativeLayoutSpecSnapshotTests.mm similarity index 98% rename from AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm rename to Tests/ASRelativeLayoutSpecSnapshotTests.mm index 71d5993158..33abb5d443 100644 --- a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm +++ b/Tests/ASRelativeLayoutSpecSnapshotTests.mm @@ -23,14 +23,6 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}}; #pragma mark - XCTestCase -- (void)setUp -{ - [super setUp]; - - self.recordMode = NO; -} - - - (void)testWithOptions { [self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionStart]; diff --git a/AsyncDisplayKitTests/ASSnapshotTestCase.h b/Tests/ASSnapshotTestCase.h similarity index 74% rename from AsyncDisplayKitTests/ASSnapshotTestCase.h rename to Tests/ASSnapshotTestCase.h index d3f6791d86..501b523a3d 100644 --- a/AsyncDisplayKitTests/ASSnapshotTestCase.h +++ b/Tests/ASSnapshotTestCase.h @@ -19,12 +19,6 @@ NSOrderedSet *ASSnapshotTestCaseDefaultSuffixes(void); { \ [ASSnapshotTestCase hackilySynchronouslyRecursivelyRenderNode:node__]; \ FBSnapshotVerifyLayerWithOptions(node__.layer, identifier__, ASSnapshotTestCaseDefaultSuffixes(), 0) \ - [node__ setShouldRasterizeDescendants:YES]; \ - [ASSnapshotTestCase hackilySynchronouslyRecursivelyRenderNode:node__]; \ - FBSnapshotVerifyLayerWithOptions(node__.layer, identifier__, ASSnapshotTestCaseDefaultSuffixes(), 0) \ - [node__ setShouldRasterizeDescendants:NO]; \ - [ASSnapshotTestCase hackilySynchronouslyRecursivelyRenderNode:node__]; \ - FBSnapshotVerifyLayerWithOptions(node__.layer, identifier__, ASSnapshotTestCaseDefaultSuffixes(), 0) \ } #define ASSnapshotVerifyLayer(layer__, identifier__) \ diff --git a/AsyncDisplayKitTests/ASSnapshotTestCase.m b/Tests/ASSnapshotTestCase.m similarity index 93% rename from AsyncDisplayKitTests/ASSnapshotTestCase.m rename to Tests/ASSnapshotTestCase.m index 0223b0bc8f..8e589c293e 100644 --- a/AsyncDisplayKitTests/ASSnapshotTestCase.m +++ b/Tests/ASSnapshotTestCase.m @@ -21,16 +21,11 @@ NSOrderedSet *ASSnapshotTestCaseDefaultSuffixes(void) // or on iOS 10 (text rasterization). If the test folders find any image that exactly matches, // they pass; if an image is not present at all, or it fails, it moves on to check the others. // This means the order doesn't matter besides reducing logging / performance. - [suffixesSet addObject:@"_32"]; - [suffixesSet addObject:@"_64"]; if (AS_AT_LEAST_IOS10) { [suffixesSet addObject:@"_iOS_10"]; } -#if __LP64__ - return [suffixesSet reversedOrderedSet]; -#else + [suffixesSet addObject:@"_64"]; return [suffixesSet copy]; -#endif } @implementation ASSnapshotTestCase diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/Tests/ASStackLayoutSpecSnapshotTests.mm similarity index 88% rename from AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm rename to Tests/ASStackLayoutSpecSnapshotTests.mm index f0438e6496..a21a37a83b 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/Tests/ASStackLayoutSpecSnapshotTests.mm @@ -22,15 +22,6 @@ @implementation ASStackLayoutSpecSnapshotTests -#pragma mark - XCTestCase - -- (void)setUp -{ - [super setUp]; - - self.recordMode = NO; -} - #pragma mark - Utility methods static NSArray *defaultSubnodes() @@ -110,6 +101,8 @@ static NSArray *defaultTextNodes() spacing:style.spacing justifyContent:style.justifyContent alignItems:style.alignItems + flexWrap:style.flexWrap + alignContent:style.alignContent children:children]; [self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; @@ -162,6 +155,28 @@ static NSArray *defaultTextNodes() [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:newSubnodes identifier:identifier]; } +- (void)testStackLayoutSpecWithAlignContent:(ASStackLayoutAlignContent)alignContent + sizeRange:(ASSizeRange)sizeRange + identifier:(NSString *)identifier +{ + ASStackLayoutSpecStyle style = { + .direction = ASStackLayoutDirectionHorizontal, + .flexWrap = ASStackLayoutFlexWrapWrap, + .alignContent = alignContent, + }; + + CGSize subnodeSize = {50, 50}; + NSArray *subnodes = @[ + ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor yellowColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor magentaColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor cyanColor], subnodeSize), + ]; + + [self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier]; +} #pragma mark - @@ -1167,4 +1182,77 @@ static NSArray *defaultTextNodes() [self testStackLayoutSpec:stackLayoutSpec sizeRange:kSize subnodes:children identifier:nil]; } +#pragma mark - Content alignment tests + +- (void)testAlignContentUnderflow +{ + // 3 lines, each line has 2 items, each item has a size of {50, 50} + // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines + static ASSizeRange kSize = {{110, 300}, {110, 300}}; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:@"alignContentStart"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:@"alignContentCenter"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:@"alignContentEnd"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:@"alignContentSpaceBetween"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:@"alignContentSpaceAround"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStretch sizeRange:kSize identifier:@"alignContentStretch"]; +} + +- (void)testAlignContentOverflow +{ + // 6 lines, each line has 1 item, each item has a size of {50, 50} + // width is 40px. It's 10px smaller than the width of each item (40px vs 50px) to test that items are still correctly collected into lines + static ASSizeRange kSize = {{40, 260}, {40, 260}}; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:@"alignContentStart"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:@"alignContentCenter"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:@"alignContentEnd"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:@"alignContentSpaceBetween"]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:@"alignContentSpaceAround"]; +} + +- (void)testAlignContentWithUnconstrainedCrossSize +{ + // 3 lines, each line has 2 items, each item has a size of {50, 50} + // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines + // height is unconstrained. It causes no cross size violation and the end results are all similar to ASStackLayoutAlignContentStart. + static ASSizeRange kSize = {{110, 0}, {110, CGFLOAT_MAX}}; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStart sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentCenter sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentEnd sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceBetween sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentSpaceAround sizeRange:kSize identifier:nil]; + [self testStackLayoutSpecWithAlignContent:ASStackLayoutAlignContentStretch sizeRange:kSize identifier:nil]; +} + +- (void)testAlignContentStretchAndOtherAlignments +{ + ASStackLayoutSpecStyle style = { + .direction = ASStackLayoutDirectionHorizontal, + .flexWrap = ASStackLayoutFlexWrapWrap, + .alignContent = ASStackLayoutAlignContentStretch, + .alignItems = ASStackLayoutAlignItemsStart, + }; + + CGSize subnodeSize = {50, 50}; + NSArray *subnodes = @[ + // 1st line + ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor yellowColor], subnodeSize), + // 2nd line + ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor magentaColor], subnodeSize), + // 3rd line + ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize), + ASDisplayNodeWithBackgroundColor([UIColor cyanColor], subnodeSize), + ]; + + subnodes[1].style.alignSelf = ASStackLayoutAlignSelfStart; + subnodes[3].style.alignSelf = ASStackLayoutAlignSelfCenter; + subnodes[5].style.alignSelf = ASStackLayoutAlignSelfEnd; + + // 3 lines, each line has 2 items, each item has a size of {50, 50} + // width is 110px. It's 10px bigger than the required width of each line (110px vs 100px) to test that items are still correctly collected into lines + static ASSizeRange kSize = {{110, 300}, {110, 300}}; + [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; +} + @end diff --git a/AsyncDisplayKitTests/ASTableViewTests.mm b/Tests/ASTableViewTests.mm similarity index 93% rename from AsyncDisplayKitTests/ASTableViewTests.mm rename to Tests/ASTableViewTests.mm index 8717211a33..8aaf9e8515 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.mm +++ b/Tests/ASTableViewTests.mm @@ -395,29 +395,6 @@ [self triggerSizeChangeAndAssertRelayoutAllNodesForTableView:tableView newSize:tableViewFinalSize]; } -- (void)testRelayoutAllNodesWithZeroSizeInitially -{ - // Initial width of the table view is 0. The first size change is part of the initial config. - // Any subsequence size change after that must trigger a relayout. - CGSize tableViewFinalSize = CGSizeMake(100, 500); - ASTestTableView *tableView = [[ASTestTableView alloc] __initWithFrame:CGRectZero - style:UITableViewStylePlain]; - ASTableViewFilledDataSource *dataSource = [ASTableViewFilledDataSource new]; - - tableView.asyncDelegate = dataSource; - tableView.asyncDataSource = dataSource; - - // Initial configuration - UIView *superview = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 500, 500)]; - [superview addSubview:tableView]; - // Width and height are swapped so that a later size change will simulate a rotation - tableView.frame = CGRectMake(0, 0, tableViewFinalSize.height, tableViewFinalSize.width); - [tableView layoutIfNeeded]; - - XCTAssertEqual(tableView.testDataController.numberOfAllNodesRelayouts, 0); - [self triggerSizeChangeAndAssertRelayoutAllNodesForTableView:tableView newSize:tableViewFinalSize]; -} - - (void)testRelayoutVisibleRowsWhenEditingModeIsChanged { CGSize tableViewSize = CGSizeMake(100, 500); @@ -567,6 +544,9 @@ XCTFail(@"Expectation failed: %@", error); } }]; + [tableView setNeedsLayout]; + [tableView layoutIfNeeded]; + [tableView waitUntilAllUpdatesAreCommitted]; } - (void)triggerSizeChangeAndAssertRelayoutAllNodesForTableView:(ASTestTableView *)tableView newSize:(CGSize)newSize @@ -628,13 +608,8 @@ // Assert that the beginning of the call pattern is correct. // There is currently noise that comes after that we will allow for this test. - NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)), - NSStringFromSelector(@selector(beginUpdates)), - NSStringFromSelector(@selector(insertSections:withRowAnimation:)), - NSStringFromSelector(@selector(insertRowsAtIndexPaths:withRowAnimation:)), - NSStringFromSelector(@selector(endUpdates))]; - NSArray *firstSelectors = [selectors subarrayWithRange:NSMakeRange(0, expectedSelectors.count)]; - XCTAssertEqualObjects(firstSelectors, expectedSelectors); + NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)) ]; + XCTAssertEqualObjects(selectors, expectedSelectors); [UITableView deswizzleAllInstanceMethods]; } @@ -662,14 +637,8 @@ // Assert that the beginning of the call pattern is correct. // There is currently noise that comes after that we will allow for this test. - NSArray *expectedSelectors = @[NSStringFromSelector(@selector(reloadData)), - NSStringFromSelector(@selector(beginUpdates)), - NSStringFromSelector(@selector(deleteSections:withRowAnimation:)), - NSStringFromSelector(@selector(insertSections:withRowAnimation:)), - NSStringFromSelector(@selector(insertRowsAtIndexPaths:withRowAnimation:)), - NSStringFromSelector(@selector(endUpdates))]; - NSArray *firstSelectors = [selectors subarrayWithRange:NSMakeRange(0, expectedSelectors.count)]; - XCTAssertEqualObjects(firstSelectors, expectedSelectors); + NSArray *expectedSelectors = @[ NSStringFromSelector(@selector(reloadData)) ]; + XCTAssertEqualObjects(selectors, expectedSelectors); [UITableView deswizzleAllInstanceMethods]; } @@ -720,6 +689,12 @@ // Trigger data load XCTAssertGreaterThan(node.numberOfSections, 0); XCTAssertGreaterThan([node numberOfRowsInSection:0], 0); + + // UITableView's section index view is added only after some rows were inserted to the table. + // All nodes loaded and measured during the initial reloadData used an outdated constrained width (i.e full width: 320). + // So we need to force a new layout pass so that the table will pick up a new constrained size and apply to its node. + [node setNeedsLayout]; + [node.view layoutIfNeeded]; [node waitUntilAllUpdatesAreCommitted]; UITableViewCell *cell = [node.view cellForRowAtIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]; @@ -820,6 +795,35 @@ } } +- (void)testAutomaticallyAdjustingContentOffset +{ + ASTableNode *node = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain]; + node.view.automaticallyAdjustsContentOffset = YES; + node.bounds = CGRectMake(0, 0, 100, 100); + ASTableViewFilledDataSource *ds = [[ASTableViewFilledDataSource alloc] init]; + node.dataSource = ds; + + [node.view layoutIfNeeded]; + [node waitUntilAllUpdatesAreCommitted]; + CGFloat rowHeight = [node.view rectForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]].size.height; + // Scroll to row (0,1) + 10pt + node.view.contentOffset = CGPointMake(0, rowHeight + 10); + + [node performBatchAnimated:NO updates:^{ + // Delete row 0 from all sections. + // This is silly but it's a consequence of how ASTableViewFilledDataSource is built. + ds.rowsPerSection -= 1; + for (NSInteger i = 0; i < NumberOfSections; i++) { + [node deleteRowsAtIndexPaths:@[ [NSIndexPath indexPathForItem:0 inSection:i]] withRowAnimation:UITableViewRowAnimationAutomatic]; + } + } completion:nil]; + [node waitUntilAllUpdatesAreCommitted]; + + // Now that row (0,0) is deleted, we should have slid up to be at just 10 + // i.e. we should have subtracted the deleted row height from our content offset. + XCTAssertEqual(node.view.contentOffset.y, 10); +} + @end @implementation UITableView (Testing) diff --git a/AsyncDisplayKitTests/ASTableViewThrashTests.m b/Tests/ASTableViewThrashTests.m similarity index 99% rename from AsyncDisplayKitTests/ASTableViewThrashTests.m rename to Tests/ASTableViewThrashTests.m index f45ea98602..ac91042b89 100644 --- a/AsyncDisplayKitTests/ASTableViewThrashTests.m +++ b/Tests/ASTableViewThrashTests.m @@ -263,7 +263,7 @@ static volatile int32_t ASThrashTestSectionNextID = 1; - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath { ASThrashTestNode *node = [[ASThrashTestNode alloc] init]; - node.item = self.data[indexPath.section].items[indexPath.item];; + node.item = self.data[indexPath.section].items[indexPath.item]; [self.allNodes addObject:node]; return node; } diff --git a/AsyncDisplayKitTests/ASTextKitCoreTextAdditionsTests.m b/Tests/ASTextKitCoreTextAdditionsTests.m similarity index 100% rename from AsyncDisplayKitTests/ASTextKitCoreTextAdditionsTests.m rename to Tests/ASTextKitCoreTextAdditionsTests.m diff --git a/AsyncDisplayKitTests/ASTextKitTests.mm b/Tests/ASTextKitTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASTextKitTests.mm rename to Tests/ASTextKitTests.mm diff --git a/AsyncDisplayKitTests/ASTextKitTruncationTests.mm b/Tests/ASTextKitTruncationTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASTextKitTruncationTests.mm rename to Tests/ASTextKitTruncationTests.mm diff --git a/AsyncDisplayKitTests/ASTextNodePerformanceTests.m b/Tests/ASTextNodePerformanceTests.m similarity index 100% rename from AsyncDisplayKitTests/ASTextNodePerformanceTests.m rename to Tests/ASTextNodePerformanceTests.m diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/Tests/ASTextNodeSnapshotTests.m similarity index 100% rename from AsyncDisplayKitTests/ASTextNodeSnapshotTests.m rename to Tests/ASTextNodeSnapshotTests.m diff --git a/AsyncDisplayKitTests/ASTextNodeTests.m b/Tests/ASTextNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASTextNodeTests.m rename to Tests/ASTextNodeTests.m diff --git a/AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm b/Tests/ASTextNodeWordKernerTests.mm similarity index 100% rename from AsyncDisplayKitTests/ASTextNodeWordKernerTests.mm rename to Tests/ASTextNodeWordKernerTests.mm diff --git a/AsyncDisplayKitTests/ASUICollectionViewTests.m b/Tests/ASUICollectionViewTests.m similarity index 100% rename from AsyncDisplayKitTests/ASUICollectionViewTests.m rename to Tests/ASUICollectionViewTests.m diff --git a/AsyncDisplayKitTests/ASVideoNodeTests.m b/Tests/ASVideoNodeTests.m similarity index 100% rename from AsyncDisplayKitTests/ASVideoNodeTests.m rename to Tests/ASVideoNodeTests.m diff --git a/AsyncDisplayKitTests/ASViewControllerTests.m b/Tests/ASViewControllerTests.m similarity index 100% rename from AsyncDisplayKitTests/ASViewControllerTests.m rename to Tests/ASViewControllerTests.m diff --git a/AsyncDisplayKitTests/ASWeakMapTests.m b/Tests/ASWeakMapTests.m similarity index 100% rename from AsyncDisplayKitTests/ASWeakMapTests.m rename to Tests/ASWeakMapTests.m diff --git a/AsyncDisplayKitTests/ASWeakSetTests.m b/Tests/ASWeakSetTests.m similarity index 100% rename from AsyncDisplayKitTests/ASWeakSetTests.m rename to Tests/ASWeakSetTests.m diff --git a/AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm b/Tests/ASWrapperSpecSnapshotTests.mm similarity index 96% rename from AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm rename to Tests/ASWrapperSpecSnapshotTests.mm index b232d9bb25..21619348af 100644 --- a/AsyncDisplayKitTests/ASWrapperSpecSnapshotTests.mm +++ b/Tests/ASWrapperSpecSnapshotTests.mm @@ -17,13 +17,6 @@ @implementation ASWrapperSpecSnapshotTests -- (void)setUp -{ - [super setUp]; - - self.recordMode = NO; -} - - (void)testWrapperSpecWithOneElementShouldSizeToElement { ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50}); diff --git a/AsyncDisplayKitTests/ASXCTExtensions.h b/Tests/ASXCTExtensions.h similarity index 100% rename from AsyncDisplayKitTests/ASXCTExtensions.h rename to Tests/ASXCTExtensions.h diff --git a/AsyncDisplayKitTests/ArrayDiffingTests.m b/Tests/ArrayDiffingTests.m similarity index 100% rename from AsyncDisplayKitTests/ArrayDiffingTests.m rename to Tests/ArrayDiffingTests.m diff --git a/AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist b/Tests/AsyncDisplayKitTests-Info.plist similarity index 100% rename from AsyncDisplayKitTests/AsyncDisplayKitTests-Info.plist rename to Tests/AsyncDisplayKitTests-Info.plist diff --git a/AsyncDisplayKitTests/AsyncDisplayKitTests-Prefix.pch b/Tests/AsyncDisplayKitTests-Prefix.pch similarity index 100% rename from AsyncDisplayKitTests/AsyncDisplayKitTests-Prefix.pch rename to Tests/AsyncDisplayKitTests-Prefix.pch diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png b/Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png rename to Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testChildrenMeasuredWithAutoMaxSize@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png b/Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png rename to Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_overflowChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png b/Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png rename to Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_underflowChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png b/Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png rename to Tests/ReferenceImages_64/ASAbsoluteLayoutSpecSnapshotTests/testSizingBehaviour_wrappedChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@2x.png b/Tests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@2x.png rename to Tests/ReferenceImages_64/ASBackgroundLayoutSpecSnapshotTests/testBackground@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotCentering@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringX@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringXCenteringY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithOptions_CenteringY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumX@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumXSizingMinimumY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@2x.png b/Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@2x.png rename to Tests/ReferenceImages_64/ASCenterLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASDisplayNodeSnapshotTests/testBasicHierarchySnapshotTesting@2x.png b/Tests/ReferenceImages_64/ASDisplayNodeSnapshotTests/testBasicHierarchySnapshotTesting@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASDisplayNodeSnapshotTests/testBasicHierarchySnapshotTesting@2x.png rename to Tests/ReferenceImages_64/ASDisplayNodeSnapshotTests/testBasicHierarchySnapshotTesting@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_first@2x.png b/Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_first@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_first@2x.png rename to Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_first@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_second@2x.png b/Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_second@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_second@2x.png rename to Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testForcedScaling_second@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASImageNodeSnapshotTests/testRenderLogoSquare@2x.png b/Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testRenderLogoSquare@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASImageNodeSnapshotTests/testRenderLogoSquare@2x.png rename to Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testRenderLogoSquare@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testRoundedCornerBlock@2x.png b/Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testRoundedCornerBlock@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testRoundedCornerBlock@2x.png rename to Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testRoundedCornerBlock@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testTintColorBlock@2x.png b/Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testTintColorBlock@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASImageNodeSnapshotTests/testTintColorBlock@2x.png rename to Tests/ReferenceImages_64/ASImageNodeSnapshotTests/testTintColorBlock@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-10-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_10-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-10-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithFixedSize_inf-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-0-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-0-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-0-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_0-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-0-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-0-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-0-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-0@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithInfinityAndZeroInsetValue_inf-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-10-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_10-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-10-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-10-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-10@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@2x.png b/Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@2x.png rename to Tests/ReferenceImages_64/ASInsetLayoutSpecSnapshotTests/testInsetsWithVariableSize_inf-inf-inf-inf@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@2x.png b/Tests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@2x.png rename to Tests/ReferenceImages_64/ASOverlayLayoutSpecSnapshotTests/testOverlay@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@2x.png b/Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@2x.png rename to Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_DoubleRatio@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@2x.png b/Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@2x.png rename to Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_HalfRatio@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@2x.png b/Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@2x.png rename to Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_SevenTimesRatio@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@2x.png b/Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@2x.png rename to Tests/ReferenceImages_64/ASRatioLayoutSpecSnapshotTests/testRatioLayout_TenTimesRatioWithItemTooBig@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testMinimumSizeRangeIsGivenToChildWhenNotPositioning@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterX@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXCenterY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterXEndY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_CenterY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndX@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXCenterY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_32/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndXEndY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithOptions_EndY@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidth@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png b/Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png rename to Tests/ReferenceImages_64/ASRelativeLayoutSpecSnapshotTests/testWithSizingOptions_SizingMinimumWidthSizingMinimumHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithFlexedMainDimension@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignCenterWithIndefiniteCrossDimension@2x.png diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png new file mode 100644 index 0000000000..032e9fac3a Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentCenter@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png new file mode 100644 index 0000000000..6d08d18e4a Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentEnd@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png new file mode 100644 index 0000000000..032e9fac3a Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceAround@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png new file mode 100644 index 0000000000..4df2a81184 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentSpaceBetween@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png new file mode 100644 index 0000000000..4df2a81184 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentOverflow_alignContentStart@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png new file mode 100644 index 0000000000..51d060062c Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentStretchAndOtherAlignments@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png new file mode 100644 index 0000000000..fea7203a26 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentCenter@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png new file mode 100644 index 0000000000..9e0bdad429 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentEnd@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png new file mode 100644 index 0000000000..bb608622fd Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceAround@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png new file mode 100644 index 0000000000..17b8dbfbc4 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentSpaceBetween@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png new file mode 100644 index 0000000000..ab969dd638 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStart@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png new file mode 100644 index 0000000000..584aca77db Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentUnderflow_alignContentStretch@2x.png differ diff --git a/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png new file mode 100644 index 0000000000..12936781c0 Binary files /dev/null and b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignContentWithUnconstrainedCrossSize@2x.png differ diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedCenter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedEnd@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStart@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchNoChildExceedsMin@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testAlignedStretchOneChildExceedsMin@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingAfter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBalancedOut@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildSpacing_spacingBefore@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testChildThatChangesCrossSizeWhenMainSizeIsFlexed@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_fixedHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisSizeBehaviors_variableHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testCrossAxisStretchingOccursAfterStackAxisFlexing@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testEmptyStack@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_overflow@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisAppliedWhenFlexingItems_underflow@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFixedFlexBasisOverridesIntrinsicSizeForNonFlexingChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_overflow@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFlexWithUnequalIntrinsicSizes_underflow@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testFractionalFlexBasisResolvesAgainstParentSize@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalBottomRight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalCenter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_horizontalTopLeft@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalBottomRight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalCenter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testHorizontalAndVerticalAlignments_verticalTopLeft@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedCenterWithChildSpacing_variableHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithOneChild@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceAroundWithRemainingSpace@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithOneChild@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testJustifiedSpaceBetweenWithRemainingSpace@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSize@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAmongMixedChildrenWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactor@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorAmongMixedChildrenArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZero@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorDoesNotShrinkToZeroWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeAndFlexFactorWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNegativeViolationIsDistributedBasedOnSizeWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testNestedStackLayoutStretchDoesNotViolateWidth@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviorsWhenAllFlexShrinkChildrenHaveBeenClampedToZeroButViolationStillExists@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_flex@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyCenter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyEnd@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testOverflowBehaviors_justifyStart@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEqually@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyAmongMixedChildrenWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedEquallyWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionally@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildren@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyAmongMixedChildrenWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testPositiveViolationIsDistributedProportionallyWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChild@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testRemainingViolationIsAppliedProperlyToFirstFlexibleChildWithArbitraryFloats@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacingWithChildrenHavingNilObjects_variableHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testStackSpacing_variableHeight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_flex@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyCenter@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyEnd@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceAround@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifySpaceBetween@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@2x.png b/Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@2x.png rename to Tests/ReferenceImages_64/ASStackLayoutSpecSnapshotTests/testUnderflowBehaviors_justifyStart@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testShadowing@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInset@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInset@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInset@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInset@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png b/Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png rename to Tests/ReferenceImages_64/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png b/Tests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png rename to Tests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithMultipleElementsShouldSizeToLargestElement@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png b/Tests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png rename to Tests/ReferenceImages_64/ASWrapperSpecSnapshotTests/testWrapperSpecWithOneElementShouldSizeToElement@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png b/Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png rename to Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithSpaceBetween@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png b/Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png rename to Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignmentWithStretchedItem@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png b/Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png rename to Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineFirst@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png b/Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png rename to Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testBaselineAlignment_baselineLast@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png b/Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png rename to Tests/ReferenceImages_iOS_10/ASStackLayoutSpecSnapshotTests/testNestedBaselineAlignments@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testShadowing@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInset@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInset@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInset@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInset@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetHighlight@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testTextContainerInsetIsIncludedWithSmallerConstrainedSize@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatFastPathTruncationWorks@2x.png diff --git a/AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png b/Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png similarity index 100% rename from AsyncDisplayKitTests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png rename to Tests/ReferenceImages_iOS_10/ASTextNodeSnapshotTests/testThatSlowPathTruncationWorks@2x.png diff --git a/AsyncDisplayKitTestHost/AppDelegate.h b/Tests/TestHost/AppDelegate.h similarity index 100% rename from AsyncDisplayKitTestHost/AppDelegate.h rename to Tests/TestHost/AppDelegate.h diff --git a/AsyncDisplayKitTestHost/AppDelegate.mm b/Tests/TestHost/AppDelegate.m similarity index 100% rename from AsyncDisplayKitTestHost/AppDelegate.mm rename to Tests/TestHost/AppDelegate.m diff --git a/AsyncDisplayKitTestHost/Info.plist b/Tests/TestHost/Info.plist similarity index 100% rename from AsyncDisplayKitTestHost/Info.plist rename to Tests/TestHost/Info.plist diff --git a/AsyncDisplayKitTestHost/main.m b/Tests/TestHost/main.m similarity index 100% rename from AsyncDisplayKitTestHost/main.m rename to Tests/TestHost/main.m diff --git a/AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase b/Tests/TestResources/ASThrashTestRecordedCase similarity index 100% rename from AsyncDisplayKitTests/TestResources/ASThrashTestRecordedCase rename to Tests/TestResources/ASThrashTestRecordedCase diff --git a/AsyncDisplayKitTests/TestResources/AttributedStringsFixture0.plist b/Tests/TestResources/AttributedStringsFixture0.plist similarity index 100% rename from AsyncDisplayKitTests/TestResources/AttributedStringsFixture0.plist rename to Tests/TestResources/AttributedStringsFixture0.plist diff --git a/AsyncDisplayKitTests/TestResources/logo-square.png b/Tests/TestResources/logo-square.png similarity index 100% rename from AsyncDisplayKitTests/TestResources/logo-square.png rename to Tests/TestResources/logo-square.png diff --git a/AsyncDisplayKitTests/en.lproj/InfoPlist.strings b/Tests/en.lproj/InfoPlist.strings similarity index 100% rename from AsyncDisplayKitTests/en.lproj/InfoPlist.strings rename to Tests/en.lproj/InfoPlist.strings diff --git a/examples/ASDKLayoutTransition/Sample/ViewController.m b/examples/ASDKLayoutTransition/Sample/ViewController.m index 71f025cd69..dd8d375d13 100644 --- a/examples/ASDKLayoutTransition/Sample/ViewController.m +++ b/examples/ASDKLayoutTransition/Sample/ViewController.m @@ -53,6 +53,7 @@ _textNodeTwo = [[ASTextNode alloc] init]; _textNodeTwo.attributedText = [[NSAttributedString alloc] initWithString:@"It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English."]; + ASSetDebugNames(_textNodeOne, _textNodeTwo); // Setup button NSString *buttonTitle = @"Start Layout Transition"; diff --git a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj index 905ef9e41f..aab6d55e44 100644 --- a/examples/ASDKgram/Sample.xcodeproj/project.pbxproj +++ b/examples/ASDKgram/Sample.xcodeproj/project.pbxproj @@ -36,12 +36,14 @@ CC5532171E15CC1E0011C01F /* ASCollectionSectionController.m in Sources */ = {isa = PBXBuildFile; fileRef = CC5532161E15CC1E0011C01F /* ASCollectionSectionController.m */; }; CC6350BB1E1C482D002BC613 /* TailLoadingNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CC6350BA1E1C482D002BC613 /* TailLoadingNode.m */; }; CC85250F1E36B392008EABE6 /* FeedHeaderNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CC85250E1E36B392008EABE6 /* FeedHeaderNode.m */; }; + E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */ = {isa = PBXBuildFile; fileRef = E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 0585427F19D4DBE100606EA6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../Default-568h@2x.png"; sourceTree = ""; }; 05E2128119D4DB510098F589 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3D24B17D1E4A4E7A9566C5E9 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 69CE83D91E515036004AA230 /* PhotoFeedControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoFeedControllerProtocol.h; sourceTree = ""; }; 6C2C82AA19EE274300767484 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = SOURCE_ROOT; }; 6C2C82AB19EE274300767484 /* Default-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-736h@3x.png"; sourceTree = SOURCE_ROOT; }; 76229A761CBB79E000B62CEF /* WindowWithStatusBarUnderlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WindowWithStatusBarUnderlay.h; sourceTree = ""; }; @@ -97,6 +99,8 @@ CC85250D1E36B392008EABE6 /* FeedHeaderNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeedHeaderNode.h; sourceTree = ""; }; CC85250E1E36B392008EABE6 /* FeedHeaderNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeedHeaderNode.m; sourceTree = ""; }; D09B5DF0BFB37583DE8F3142 /* 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 = ""; }; + E5F128EE1E09612700B4335F /* PhotoFeedBaseController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PhotoFeedBaseController.h; sourceTree = ""; }; + E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PhotoFeedBaseController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -183,6 +187,9 @@ 767A5F141CAA3D8A004CDA8D /* Controller */ = { isa = PBXGroup; children = ( + 69CE83D91E515036004AA230 /* PhotoFeedControllerProtocol.h */, + E5F128EE1E09612700B4335F /* PhotoFeedBaseController.h */, + E5F128EF1E09625400B4335F /* PhotoFeedBaseController.m */, 767A5F161CAA3D96004CDA8D /* UIKit */, 767A5F151CAA3D90004CDA8D /* ASDK */, CC00D1581E159132004E5502 /* ASDK-ListKit */, @@ -380,7 +387,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = { @@ -412,6 +419,7 @@ 768843821CAA37EF00D8629E /* CommentModel.m in Sources */, 768843831CAA37EF00D8629E /* CommentsNode.m in Sources */, 768843961CAA37EF00D8629E /* Utilities.m in Sources */, + E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */, 768843931CAA37EF00D8629E /* UserModel.m in Sources */, CC5532171E15CC1E0011C01F /* ASCollectionSectionController.m in Sources */, 768843801CAA37EF00D8629E /* AppDelegate.m in Sources */, diff --git a/examples/ASDKgram/Sample/AppDelegate.h b/examples/ASDKgram/Sample/AppDelegate.h index cd0d7065ab..f5a8485f9b 100644 --- a/examples/ASDKgram/Sample/AppDelegate.h +++ b/examples/ASDKgram/Sample/AppDelegate.h @@ -17,10 +17,6 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -@protocol PhotoFeedControllerProtocol -- (void)resetAllData; -@end - @interface AppDelegate : UIResponder @end diff --git a/examples/ASDKgram/Sample/CommentsNode.h b/examples/ASDKgram/Sample/CommentsNode.h index 3b0bbff424..2e8a9b2157 100644 --- a/examples/ASDKgram/Sample/CommentsNode.h +++ b/examples/ASDKgram/Sample/CommentsNode.h @@ -20,7 +20,7 @@ #import #import "CommentFeedModel.h" -@interface CommentsNode : ASTextCellNode +@interface CommentsNode : ASDisplayNode - (void)updateWithCommentFeedModel:(CommentFeedModel *)feed; diff --git a/examples/ASDKgram/Sample/CommentsNode.m b/examples/ASDKgram/Sample/CommentsNode.m index 67859bd37f..88d2cb78c1 100644 --- a/examples/ASDKgram/Sample/CommentsNode.m +++ b/examples/ASDKgram/Sample/CommentsNode.m @@ -96,6 +96,7 @@ for (NSUInteger i = 0; i < numLabelsToAdd; i++) { ASTextNode *commentLabel = [[ASTextNode alloc] init]; + commentLabel.layerBacked = YES; commentLabel.maximumNumberOfLines = 3; [_commentNodes addObject:commentLabel]; diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index f3f8246459..a0b13fc04b 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -100,7 +100,7 @@ _photoCommentsNode = [[CommentsNode alloc] init]; - _photoCommentsNode.shouldRasterizeDescendants = YES; + _photoCommentsNode.layerBacked = YES; // instead of adding everything addSubnode: self.automaticallyManagesSubnodes = YES; diff --git a/examples/ASDKgram/Sample/PhotoFeedBaseController.h b/examples/ASDKgram/Sample/PhotoFeedBaseController.h new file mode 100644 index 0000000000..10029863c3 --- /dev/null +++ b/examples/ASDKgram/Sample/PhotoFeedBaseController.h @@ -0,0 +1,39 @@ +// +// PhotoFeedBaseController.h +// Sample +// +// Created by Huy Nguyen on 20/12/16. +// +// 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 "PhotoFeedControllerProtocol.h" + +@protocol PhotoFeedControllerProtocol; +@class PhotoFeedModel; + +@interface PhotoFeedBaseController : ASViewController + +@property (nonatomic, strong, readonly) PhotoFeedModel *photoFeed; +@property (nonatomic, strong, readonly) UITableView *tableView; + +- (void)refreshFeed; +- (void)insertNewRows:(NSArray *)newPhotos; + +#pragma mark - Subclasses must override these methods + +- (void)loadPage; +- (void)requestCommentsForPhotos:(NSArray *)newPhotos; + +@end diff --git a/examples/ASDKgram/Sample/PhotoFeedBaseController.m b/examples/ASDKgram/Sample/PhotoFeedBaseController.m new file mode 100644 index 0000000000..c901d737e2 --- /dev/null +++ b/examples/ASDKgram/Sample/PhotoFeedBaseController.m @@ -0,0 +1,123 @@ +// +// PhotoFeedBaseController.m +// Sample +// +// Created by Huy Nguyen on 20/12/16. +// Copyright © 2016 Facebook. 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 +// 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 "PhotoFeedBaseController.h" +#import "PhotoFeedModel.h" + +@implementation PhotoFeedBaseController +{ + UIActivityIndicatorView *_activityIndicatorView; +} + +// -loadView is guaranteed to be called on the main thread and is the appropriate place to +// set up an UIKit objects you may be using. +- (void)loadView +{ + [super loadView]; + + _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + + _photoFeed = [[PhotoFeedModel alloc] initWithPhotoFeedModelType:PhotoFeedModelTypePopular imageSize:[self imageSizeForScreenWidth]]; + [self refreshFeed]; + + CGSize boundSize = self.view.bounds.size; + [_activityIndicatorView sizeToFit]; + CGRect refreshRect = _activityIndicatorView.frame; + refreshRect.origin = CGPointMake((boundSize.width - _activityIndicatorView.frame.size.width) / 2.0, + (boundSize.height - _activityIndicatorView.frame.size.height) / 2.0); + _activityIndicatorView.frame = refreshRect; + [self.view addSubview:_activityIndicatorView]; + + self.tableView.allowsSelection = NO; + self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (void)refreshFeed +{ + [_activityIndicatorView startAnimating]; + // small first batch + [_photoFeed refreshFeedWithCompletionBlock:^(NSArray *newPhotos){ + + [_activityIndicatorView stopAnimating]; + + [self insertNewRows:newPhotos]; + [self requestCommentsForPhotos:newPhotos]; + + // immediately start second larger fetch + [self loadPage]; + + } numResultsToReturn:4]; +} + +- (void)insertNewRows:(NSArray *)newPhotos +{ + NSInteger section = 0; + NSMutableArray *indexPaths = [NSMutableArray array]; + + NSInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed]; + for (NSInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) { + NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section]; + [indexPaths addObject:path]; + } + [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; +} + +- (UIStatusBarStyle)preferredStatusBarStyle +{ + return UIStatusBarStyleLightContent; +} + +- (CGSize)imageSizeForScreenWidth +{ + CGRect screenRect = [[UIScreen mainScreen] bounds]; + CGFloat screenScale = [[UIScreen mainScreen] scale]; + return CGSizeMake(screenRect.size.width * screenScale, screenRect.size.width * screenScale); +} + +#pragma mark - PhotoFeedViewControllerProtocol + +- (void)resetAllData +{ + [_photoFeed clearFeed]; + [self.tableView reloadData]; + [self refreshFeed]; +} + +#pragma mark - Subclassing + +- (UITableView *)tableView +{ + NSAssert(NO, @"Subclasses must override this method"); + return nil; +} + +- (void)loadPage +{ + NSAssert(NO, @"Subclasses must override this method"); +} + +- (void)requestCommentsForPhotos:(NSArray *)newPhotos +{ + NSAssert(NO, @"Subclasses must override this method"); +} + +@end diff --git a/examples/ASDKgram/Sample/PhotoFeedControllerProtocol.h b/examples/ASDKgram/Sample/PhotoFeedControllerProtocol.h new file mode 100644 index 0000000000..e7c5be3689 --- /dev/null +++ b/examples/ASDKgram/Sample/PhotoFeedControllerProtocol.h @@ -0,0 +1,13 @@ +// +// PhotoFeedControllerProtocol.h +// Sample +// +// Created by Michael Schneider on 2/12/17. +// Copyright © 2017 Facebook. All rights reserved. +// + +#import + +@protocol PhotoFeedControllerProtocol +- (void)resetAllData; +@end diff --git a/examples/ASDKgram/Sample/PhotoFeedListKitViewController.h b/examples/ASDKgram/Sample/PhotoFeedListKitViewController.h index 7e5c64886d..0720363a4c 100644 --- a/examples/ASDKgram/Sample/PhotoFeedListKitViewController.h +++ b/examples/ASDKgram/Sample/PhotoFeedListKitViewController.h @@ -7,7 +7,7 @@ // #import -#import "AppDelegate.h" +#import "PhotoFeedControllerProtocol.h" @interface PhotoFeedListKitViewController : ASViewController diff --git a/examples/ASDKgram/Sample/PhotoFeedNodeController.h b/examples/ASDKgram/Sample/PhotoFeedNodeController.h index 04c9aa896c..c97576b7bc 100644 --- a/examples/ASDKgram/Sample/PhotoFeedNodeController.h +++ b/examples/ASDKgram/Sample/PhotoFeedNodeController.h @@ -17,9 +17,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import -#import "AppDelegate.h" +#import "PhotoFeedBaseController.h" -@interface PhotoFeedNodeController : ASViewController +@interface PhotoFeedNodeController : PhotoFeedBaseController @end diff --git a/examples/ASDKgram/Sample/PhotoFeedNodeController.m b/examples/ASDKgram/Sample/PhotoFeedNodeController.m index 322c9de49a..1e22925e6d 100644 --- a/examples/ASDKgram/Sample/PhotoFeedNodeController.m +++ b/examples/ASDKgram/Sample/PhotoFeedNodeController.m @@ -31,9 +31,7 @@ @implementation PhotoFeedNodeController { - PhotoFeedModel *_photoFeed; - ASTableNode *_tableNode; - UIActivityIndicatorView *_activityIndicatorView; + ASTableNode *_tableNode; } #pragma mark - Lifecycle @@ -51,7 +49,6 @@ _tableNode.dataSource = self; _tableNode.delegate = self; - } return self; @@ -63,112 +60,48 @@ { [super loadView]; - _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - - _photoFeed = [[PhotoFeedModel alloc] initWithPhotoFeedModelType:PhotoFeedModelTypePopular imageSize:[self imageSizeForScreenWidth]]; - [self refreshFeed]; - - CGSize boundSize = self.view.bounds.size; - - [_activityIndicatorView sizeToFit]; - CGRect refreshRect = _activityIndicatorView.frame; - refreshRect.origin = CGPointMake((boundSize.width - _activityIndicatorView.frame.size.width) / 2.0, - (boundSize.height - _activityIndicatorView.frame.size.height) / 2.0); - _activityIndicatorView.frame = refreshRect; - - [self.view addSubview:_activityIndicatorView]; - - self.view.backgroundColor = [UIColor whiteColor]; - _tableNode.view.allowsSelection = NO; - _tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone; _tableNode.view.leadingScreensForBatching = AUTO_TAIL_LOADING_NUM_SCREENFULS; // overriding default of 2.0 } -#pragma mark - helper methods - -- (void)refreshFeed -{ - [_activityIndicatorView startAnimating]; - // small first batch - [_photoFeed refreshFeedWithCompletionBlock:^(NSArray *newPhotos){ - - [_activityIndicatorView stopAnimating]; - - [self insertNewRowsInTableNode:newPhotos]; -// [self requestCommentsForPhotos:newPhotos]; - - // immediately start second larger fetch - [self loadPageWithContext:nil]; - - } numResultsToReturn:4]; -} - - (void)loadPageWithContext:(ASBatchContext *)context { - [_photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){ + [self.photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){ - [self insertNewRowsInTableNode:newPhotos]; -// [self requestCommentsForPhotos:newPhotos]; + [self insertNewRows:newPhotos]; + [self requestCommentsForPhotos:newPhotos]; if (context) { [context completeBatchFetching:YES]; } } numResultsToReturn:20]; } -//- (void)requestCommentsForPhotos:(NSArray *)newPhotos -//{ -// for (PhotoModel *photo in newPhotos) { -// [photo.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) { -// -// NSInteger rowNum = [_photoFeed indexOfPhotoModel:photo]; -// NSIndexPath *cellPath = [NSIndexPath indexPathForRow:rowNum inSection:0]; -// PhotoCellNode *cell = (PhotoCellNode *)[_tableNode.view nodeForRowAtIndexPath:cellPath]; -// -// if (cell) { -// [cell loadCommentsForPhoto:photo]; -// [_tableNode.view beginUpdates]; -// [_tableNode.view endUpdates]; -// } -// }]; -// } -//} +#pragma mark - Subclassing -- (void)insertNewRowsInTableNode:(NSArray *)newPhotos +- (UITableView *)tableView { - NSInteger section = 0; - NSMutableArray *indexPaths = [NSMutableArray array]; - - NSUInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed]; - for (NSUInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) { - NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section]; - [indexPaths addObject:path]; - } - - [_tableNode insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; + return _tableNode.view; } -- (UIStatusBarStyle)preferredStatusBarStyle +- (void)loadPage { - return UIStatusBarStyleLightContent; + [self loadPageWithContext:nil]; } -- (CGSize)imageSizeForScreenWidth +- (void)requestCommentsForPhotos:(NSArray *)newPhotos { - CGRect screenRect = [[UIScreen mainScreen] bounds]; - CGFloat screenScale = [[UIScreen mainScreen] scale]; - return CGSizeMake(screenRect.size.width * screenScale, screenRect.size.width * screenScale); + // Do nothing (#1530). } #pragma mark - ASTableDataSource methods - (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { - return [_photoFeed numberOfItemsInFeed]; + return [self.photoFeed numberOfItemsInFeed]; } - (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { - PhotoModel *photoModel = [_photoFeed objectAtIndex:indexPath.row]; + PhotoModel *photoModel = [self.photoFeed objectAtIndex:indexPath.row]; // this will be executed on a background thread - important to make sure it's thread safe ASCellNode *(^ASCellNodeBlock)() = ^ASCellNode *() { PhotoCellNode *cellNode = [[PhotoCellNode alloc] initWithPhotoObject:photoModel]; @@ -187,13 +120,4 @@ [self loadPageWithContext:context]; } -#pragma mark - PhotoFeedViewControllerProtocol - -- (void)resetAllData -{ - [_photoFeed clearFeed]; - [_tableNode reloadData]; - [self refreshFeed]; -} - @end diff --git a/examples/ASDKgram/Sample/PhotoFeedSectionController.m b/examples/ASDKgram/Sample/PhotoFeedSectionController.m index 963eba95df..190a817853 100644 --- a/examples/ASDKgram/Sample/PhotoFeedSectionController.m +++ b/examples/ASDKgram/Sample/PhotoFeedSectionController.m @@ -36,9 +36,15 @@ [self setItems:_photoFeed.photos animated:NO completion:nil]; } +- (__kindof UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index +{ + return [ASIGListSectionControllerMethods cellForItemAtIndex:index sectionController:self]; +} -ASIGSectionControllerSizeForItemImplementation; -ASIGSectionControllerCellForIndexImplementation; +- (CGSize)sizeForItemAtIndex:(NSInteger)index +{ + return [ASIGListSectionControllerMethods sizeForItemAtIndex:index]; +} - (void)didSelectItemAtIndex:(NSInteger)index { @@ -96,10 +102,12 @@ ASIGSectionControllerCellForIndexImplementation; #pragma mark - ASSupplementaryNodeSource -- (ASCellNode *)nodeForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index +- (ASCellNodeBlock)nodeBlockForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index { ASDisplayNodeAssert([elementKind isEqualToString:UICollectionElementKindSectionHeader], nil); - return [[FeedHeaderNode alloc] init]; + return ^{ + return [[FeedHeaderNode alloc] init]; + }; } - (ASSizeRange)sizeRangeForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index @@ -118,7 +126,14 @@ ASIGSectionControllerCellForIndexImplementation; return @[ UICollectionElementKindSectionHeader ]; } -ASIGSupplementarySourceViewForSupplementaryElementImplementation(self); -ASIGSupplementarySourceSizeForSupplementaryElementImplementation; +- (__kindof UICollectionReusableView *)viewForSupplementaryElementOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + return [ASIGListSupplementaryViewSourceMethods viewForSupplementaryElementOfKind:elementKind atIndex:index sectionController:self]; +} + +- (CGSize)sizeForSupplementaryViewOfKind:(NSString *)elementKind atIndex:(NSInteger)index +{ + return [ASIGListSupplementaryViewSourceMethods sizeForSupplementaryViewOfKind:elementKind atIndex:index]; +} @end diff --git a/examples/ASDKgram/Sample/PhotoFeedViewController.h b/examples/ASDKgram/Sample/PhotoFeedViewController.h index 3942ec5c27..3733fde82b 100644 --- a/examples/ASDKgram/Sample/PhotoFeedViewController.h +++ b/examples/ASDKgram/Sample/PhotoFeedViewController.h @@ -17,8 +17,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -#import "AppDelegate.h" +#import "PhotoFeedBaseController.h" -@interface PhotoFeedViewController : UIViewController +@interface PhotoFeedViewController : PhotoFeedBaseController @end diff --git a/examples/ASDKgram/Sample/PhotoFeedViewController.m b/examples/ASDKgram/Sample/PhotoFeedViewController.m index ddaf2a7cf9..3a36861e96 100644 --- a/examples/ASDKgram/Sample/PhotoFeedViewController.m +++ b/examples/ASDKgram/Sample/PhotoFeedViewController.m @@ -30,9 +30,7 @@ @implementation PhotoFeedViewController { - PhotoFeedModel *_photoFeed; - UITableView *_tableView; - UIActivityIndicatorView *_activityIndicatorView; + UITableView *_tableView; } #pragma mark - Lifecycle @@ -49,8 +47,6 @@ _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth; _tableView.delegate = self; _tableView.dataSource = self; - - _activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; } return self; @@ -61,51 +57,22 @@ { [super viewDidLoad]; - _photoFeed = [[PhotoFeedModel alloc] initWithPhotoFeedModelType:PhotoFeedModelTypePopular imageSize:[self imageSizeForScreenWidth]]; - [self refreshFeed]; - - CGSize boundSize = self.view.bounds.size; - [self.view addSubview:_tableView]; - _tableView.frame = self.view.bounds; - _tableView.allowsSelection = NO; - _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; [_tableView registerClass:[PhotoTableViewCell class] forCellReuseIdentifier:@"photoCell"]; - - [self.view addSubview:_activityIndicatorView]; - - [_activityIndicatorView sizeToFit]; - CGRect refreshRect = _activityIndicatorView.frame; - refreshRect.origin = CGPointMake((boundSize.width - _activityIndicatorView.frame.size.width) / 2.0, - (boundSize.height - _activityIndicatorView.frame.size.height) / 2.0); - _activityIndicatorView.frame = refreshRect; } -#pragma mark - helper methods +#pragma mark - Subclassing -- (void)refreshFeed +- (UITableView *)tableView { - [_activityIndicatorView startAnimating]; - - // small first batch - [_photoFeed refreshFeedWithCompletionBlock:^(NSArray *newPhotos){ - - [_activityIndicatorView stopAnimating]; - - [self insertNewRowsInTableView:newPhotos]; - [self requestCommentsForPhotos:newPhotos]; - - // immediately start second larger fetch - [self loadPage]; - - } numResultsToReturn:4]; + return _tableView; } - (void)loadPage { - [_photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){ - [self insertNewRowsInTableView:newPhotos]; + [self.photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){ + [self insertNewRows:newPhotos]; [self requestCommentsForPhotos:newPhotos]; } numResultsToReturn:20]; } @@ -115,7 +82,7 @@ for (PhotoModel *photo in newPhotos) { [photo.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) { - NSInteger rowNum = [_photoFeed indexOfPhotoModel:photo]; + NSInteger rowNum = [self.photoFeed indexOfPhotoModel:photo]; NSIndexPath *cellPath = [NSIndexPath indexPathForRow:rowNum inSection:0]; PhotoTableViewCell *cell = [_tableView cellForRowAtIndexPath:cellPath]; @@ -135,49 +102,24 @@ } } -- (void)insertNewRowsInTableView:(NSArray *)newPhotos -{ - NSInteger section = 0; - NSMutableArray *indexPaths = [NSMutableArray array]; - - NSInteger newTotalNumberOfPhotos = [_photoFeed numberOfItemsInFeed]; - for (NSInteger row = newTotalNumberOfPhotos - newPhotos.count; row < newTotalNumberOfPhotos; row++) { - NSIndexPath *path = [NSIndexPath indexPathForRow:row inSection:section]; - [indexPaths addObject:path]; - } - [_tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone]; -} - -- (UIStatusBarStyle)preferredStatusBarStyle -{ - return UIStatusBarStyleLightContent; -} - -- (CGSize)imageSizeForScreenWidth -{ - CGRect screenRect = [[UIScreen mainScreen] bounds]; - CGFloat screenScale = [[UIScreen mainScreen] scale]; - return CGSizeMake(screenRect.size.width * screenScale, screenRect.size.width * screenScale); -} - #pragma mark - UITableViewDataSource methods - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - return [_photoFeed numberOfItemsInFeed]; + return [self.photoFeed numberOfItemsInFeed]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { PhotoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"photoCell" forIndexPath:indexPath]; - [cell updateCellWithPhotoObject:[_photoFeed objectAtIndex:indexPath.row]]; + [cell updateCellWithPhotoObject:[self.photoFeed objectAtIndex:indexPath.row]]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath { - PhotoModel *photo = [_photoFeed objectAtIndex:indexPath.row]; + PhotoModel *photo = [self.photoFeed objectAtIndex:indexPath.row]; return [PhotoTableViewCell heightForPhotoModel:photo withWidth:self.view.bounds.size.width]; } @@ -196,13 +138,4 @@ } } -#pragma mark - PhotoFeedViewControllerProtocol - -- (void)resetAllData -{ - [_photoFeed clearFeed]; - [_tableView reloadData]; - [self refreshFeed]; -} - @end diff --git a/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift b/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift index ae7db32b2f..a8770d5282 100644 --- a/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift +++ b/examples/CustomCollectionView-Swift/Sample/MosaicCollectionViewLayout.swift @@ -228,6 +228,17 @@ class MosaicCollectionViewLayoutInspector: NSObject, ASCollectionViewLayoutInspe return ASSizeRange.init(min: CGSize.zero, max: layout._headerSizeForSection(section: atIndexPath.section)) } + /** + * Asks the inspector for the number of supplementary sections in the collection view for the given kind. + */ + func collectionView(_ collectionView: ASCollectionView, numberOfSectionsForSupplementaryNodeOfKind kind: String) -> UInt { + if (kind == UICollectionElementKindSectionHeader) { + return UInt((collectionView.dataSource?.numberOfSections!(in: collectionView))!) + } else { + return 0 + } + } + /** * Asks the inspector for the number of supplementary views for the given kind in the specified section. */ @@ -240,6 +251,6 @@ class MosaicCollectionViewLayoutInspector: NSObject, ASCollectionViewLayoutInspe } func scrollableDirections() -> ASScrollDirection { - return ASScrollDirectionVerticalDirections + return ASScrollDirectionVerticalDirections; } } diff --git a/examples/PagerNode/Sample/PageNode.m b/examples/PagerNode/Sample/PageNode.m index 75f9942e7d..bda108a1bf 100644 --- a/examples/PagerNode/Sample/PageNode.m +++ b/examples/PagerNode/Sample/PageNode.m @@ -21,9 +21,9 @@ @implementation PageNode -- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize +- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize { - return [ASLayout layoutWithLayoutElement:self size:constrainedSize.max]; + return constrainedSize; } - (void)fetchData diff --git a/examples/PagerNode/Sample/ViewController.m b/examples/PagerNode/Sample/ViewController.m index 8adf45354c..001c5c069a 100644 --- a/examples/PagerNode/Sample/ViewController.m +++ b/examples/PagerNode/Sample/ViewController.m @@ -46,7 +46,7 @@ static UIColor *randomColor() { self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Next" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToNextPage:)]; self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Previous" style:UIBarButtonItemStylePlain target:self action:@selector(scrollToPreviousPage:)]; - + self.automaticallyAdjustsScrollViewInsets = NO; return self; } diff --git a/examples/README.md b/examples/README.md index 2da8c3bc54..e56667c0ce 100644 --- a/examples/README.md +++ b/examples/README.md @@ -9,7 +9,7 @@ dependencies. ### ASCollectionView [ObjC] -![ASCollectionView Example App Screenshot](./Screenshots/ASCollectionView.png?raw=true) +![ASCollectionView Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASCollectionView.png) Featuring: - ASCollectionView with header/footer supplementary node support @@ -18,27 +18,27 @@ Featuring: ### ASDKgram [ObjC] -![ASDKgram Example App Screenshot](./Screenshots/ASDKgram.png?raw=true) +![ASDKgram Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASDKgram.png) ### ASDKLayoutTransition [ObjC] -![ASDKLayoutTransition Example App](./Screenshots/ASDKLayoutTransition.gif?raw=true) +![ASDKLayoutTransition Example App](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASDKLayoutTransition.gif) ### ASDKTube [ObjC] -![ASDKTube Example App](./Screenshots/ASDKTube.gif?raw=true) +![ASDKTube Example App](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASDKTube.gif) ### ASMapNode [ObjC] -![ASMapNode Example App Screenshot](./Screenshots/ASMapNode.png?raw=true) +![ASMapNode Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASMapNode.png) ### ASTableViewStressTest [ObjC] -![ASTableViewStressTest Example App Screenshot](./Screenshots/ASTableViewStressTest.png?raw=true) +![ASTableViewStressTest Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASTableViewStressTest.png) ### ASViewController [ObjC] -![ASViewController Example App Screenshot](./Screenshots/ASViewController.png?raw=true) +![ASViewController Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/ASViewController.png) Featuring: - ASViewController @@ -48,11 +48,11 @@ Featuring: ### AsyncDisplayKitOverview [ObjC] -![AsyncDisplayKitOverview Example App Screenshot](./Screenshots/AsyncDisplayKitOverview.png?raw=true) +![AsyncDisplayKitOverview Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/AsyncDisplayKitOverview.png) ### BackgroundPropertySetting [Swift] -![BackgroundPropertySetting Example App gif](./Screenshots/BackgroundPropertySetting.gif?raw=true) +![BackgroundPropertySetting Example App gif](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/BackgroundPropertySetting.gif) Featuring: - ASDK Swift compatibility @@ -64,7 +64,7 @@ Featuring: ### CarthageBuildTest ### CatDealsCollectionView [ObjC] -![CatDealsCollectionView Example App Screenshot](./Screenshots/CatDealsCollectionView.png?raw=true) +![CatDealsCollectionView Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/CatDealsCollectionView.png) Featuring: - ASCollectionView @@ -74,7 +74,7 @@ Featuring: ### CollectionViewWithViewControllerCells [ObjC] -![CollectionViewWithViewControllerCells Example App Screenshot](./Screenshots/CollectionViewWithViewControllerCells.png?raw=true) +![CollectionViewWithViewControllerCells Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/CollectionViewWithViewControllerCells.png) Featuring: - custom collection view layout @@ -83,7 +83,7 @@ Featuring: ### CustomCollectionView [ObjC+Swift] -![CustomCollectionView Example App gif](./Screenshots/CustomCollectionView.gif?raw=true) +![CustomCollectionView Example App gif](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/CustomCollectionView.git) Featuring: - custom collection view layout @@ -91,14 +91,14 @@ Featuring: ### EditableText [ObjC] -![EditableText Example App Screenshot](./Screenshots/EditableText.png?raw=true) +![EditableText Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/EditableText.png) Featuring: - ASEditableTextNode ### HorizontalwithinVerticalScrolling [ObjC] -![HorizontalwithinVerticalScrolling Example App gif](./Screenshots/HorizontalwithinVerticalScrolling.gif?raw=true) +![HorizontalwithinVerticalScrolling Example App gif](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/HorizontalwithinVerticalScrolling.gif) Featuring: - UIViewController with ASTableView @@ -107,7 +107,7 @@ Featuring: ### Kittens [ObjC] -![Kittens Example App Screenshot](./Screenshots/Kittens.png?raw=true) +![Kittens Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/Kittens.png) Featuring: - UIViewController with ASTableView @@ -115,11 +115,11 @@ Featuring: ### LayoutSpecPlayground [ObjC] -![LayoutSpecPlayground Example App Screenshot](./Screenshots/LayoutSpecPlayground.png?raw=true) +![LayoutSpecPlayground Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/LayoutSpecPlayground.png) ### Multiplex [ObjC] -![Multiplex Example App](./Screenshots/Multiplex.gif?raw=true) +![Multiplex Example App](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/Multiplex.gif) Featuring: - ASMultiplexImageNode (with artificial delay inserted) @@ -127,7 +127,7 @@ Featuring: ### PagerNode [ObjC] -![PagerNode Example App](./Screenshots/PagerNode.gif?raw=true) +![PagerNode Example App](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/PagerNode.gif) Featuring: - ASPagerNode @@ -139,7 +139,7 @@ Featuring: ### SocialAppLayout [ObjC] -![SocialAppLayout Example App Screenshot](./Screenshots/SocialAppLayout.png?raw=true) +![SocialAppLayout Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/SocialAppLayout.png) Featuring: - ASLayoutSpec @@ -147,14 +147,14 @@ Featuring: ### Swift [Swift] -![Swift Example App Screenshot](./Screenshots/Swift.png?raw=true) +![Swift Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/Swift.png) Featuring: - ASViewController with ASTableNode ### SynchronousConcurrency [ObjC] -![SynchronousConcurrency Example App Screenshot](./Screenshots/SynchronousConcurrency.png?raw=true) +![SynchronousConcurrency Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/SynchronousConcurrency.png) Implementation of Synchronous Concurrency features for AsyncDisplayKit 2.0 @@ -182,21 +182,21 @@ cell types may be appropriate to display to the user with placeholders, whereas ### VerticalWithinHorizontalScrolling [ObjC] -![VerticalWithinHorizontalScrolling Example App](./Screenshots/VerticalWithinHorizontalScrolling.gif?raw=true) +![VerticalWithinHorizontalScrolling Example App](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/VerticalWithinHorizontalScrolling.gif) Features: - UIViewController containing ASPagerNode containing ASTableNodes ### Videos [ObjC] -![VideoTableView Example App gif](./Screenshots/Videos.gif?raw=true) +![VideoTableView Example App gif](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/Videos.gif) Featuring: - ASVideoNode ### VideoTableView [ObjC] -![VideoTableView Example App Screenshot](./Screenshots/VideoTableView.png?raw=true) +![VideoTableView Example App Screenshot](https://github.com/AsyncDisplayKit/Documentation/raw/master/docs/static/images/example-app-screenshots/VideoTableView.png) Featuring: - ASVideoNode diff --git a/examples/Screenshots/ASCollectionView.png b/examples/Screenshots/ASCollectionView.png deleted file mode 100644 index 3aaff368e5..0000000000 Binary files a/examples/Screenshots/ASCollectionView.png and /dev/null differ diff --git a/examples/Screenshots/ASDKLayoutTransition.gif b/examples/Screenshots/ASDKLayoutTransition.gif deleted file mode 100644 index c75467164d..0000000000 Binary files a/examples/Screenshots/ASDKLayoutTransition.gif and /dev/null differ diff --git a/examples/Screenshots/ASDKTube.gif b/examples/Screenshots/ASDKTube.gif deleted file mode 100644 index 956a5c0440..0000000000 Binary files a/examples/Screenshots/ASDKTube.gif and /dev/null differ diff --git a/examples/Screenshots/ASDKgram.png b/examples/Screenshots/ASDKgram.png deleted file mode 100644 index 358dae3805..0000000000 Binary files a/examples/Screenshots/ASDKgram.png and /dev/null differ diff --git a/examples/Screenshots/ASMapNode.png b/examples/Screenshots/ASMapNode.png deleted file mode 100644 index aa605349fe..0000000000 Binary files a/examples/Screenshots/ASMapNode.png and /dev/null differ diff --git a/examples/Screenshots/ASTableViewStressTest.png b/examples/Screenshots/ASTableViewStressTest.png deleted file mode 100644 index cd7aae2d9d..0000000000 Binary files a/examples/Screenshots/ASTableViewStressTest.png and /dev/null differ diff --git a/examples/Screenshots/ASViewController.png b/examples/Screenshots/ASViewController.png deleted file mode 100644 index 545f3c8d63..0000000000 Binary files a/examples/Screenshots/ASViewController.png and /dev/null differ diff --git a/examples/Screenshots/AsyncDisplayKitOverview.png b/examples/Screenshots/AsyncDisplayKitOverview.png deleted file mode 100644 index 4941292c4b..0000000000 Binary files a/examples/Screenshots/AsyncDisplayKitOverview.png and /dev/null differ diff --git a/examples/Screenshots/BackgroundPropertySetting.gif b/examples/Screenshots/BackgroundPropertySetting.gif deleted file mode 100644 index e2655ef235..0000000000 Binary files a/examples/Screenshots/BackgroundPropertySetting.gif and /dev/null differ diff --git a/examples/Screenshots/CatDealsCollectionView.png b/examples/Screenshots/CatDealsCollectionView.png deleted file mode 100644 index 72f9179e94..0000000000 Binary files a/examples/Screenshots/CatDealsCollectionView.png and /dev/null differ diff --git a/examples/Screenshots/CollectionViewWithViewControllerCells.png b/examples/Screenshots/CollectionViewWithViewControllerCells.png deleted file mode 100644 index 078b7b945f..0000000000 Binary files a/examples/Screenshots/CollectionViewWithViewControllerCells.png and /dev/null differ diff --git a/examples/Screenshots/CustomCollectionView.gif b/examples/Screenshots/CustomCollectionView.gif deleted file mode 100644 index 8d8a2aed1e..0000000000 Binary files a/examples/Screenshots/CustomCollectionView.gif and /dev/null differ diff --git a/examples/Screenshots/EditableText.png b/examples/Screenshots/EditableText.png deleted file mode 100644 index 1aa8d6db9b..0000000000 Binary files a/examples/Screenshots/EditableText.png and /dev/null differ diff --git a/examples/Screenshots/HorizontalwithinVerticalScrolling.gif b/examples/Screenshots/HorizontalwithinVerticalScrolling.gif deleted file mode 100644 index fe722a308d..0000000000 Binary files a/examples/Screenshots/HorizontalwithinVerticalScrolling.gif and /dev/null differ diff --git a/examples/Screenshots/Kittens.png b/examples/Screenshots/Kittens.png deleted file mode 100644 index 91d9bf36fa..0000000000 Binary files a/examples/Screenshots/Kittens.png and /dev/null differ diff --git a/examples/Screenshots/LayoutSpecPlayground.png b/examples/Screenshots/LayoutSpecPlayground.png deleted file mode 100644 index 9ed4db7c55..0000000000 Binary files a/examples/Screenshots/LayoutSpecPlayground.png and /dev/null differ diff --git a/examples/Screenshots/Multiplex.gif b/examples/Screenshots/Multiplex.gif deleted file mode 100644 index fcb98cad62..0000000000 Binary files a/examples/Screenshots/Multiplex.gif and /dev/null differ diff --git a/examples/Screenshots/PagerNode.gif b/examples/Screenshots/PagerNode.gif deleted file mode 100644 index 8c13a0faea..0000000000 Binary files a/examples/Screenshots/PagerNode.gif and /dev/null differ diff --git a/examples/Screenshots/SocialAppLayout.png b/examples/Screenshots/SocialAppLayout.png deleted file mode 100644 index 33d6672102..0000000000 Binary files a/examples/Screenshots/SocialAppLayout.png and /dev/null differ diff --git a/examples/Screenshots/Swift.png b/examples/Screenshots/Swift.png deleted file mode 100644 index b099a17229..0000000000 Binary files a/examples/Screenshots/Swift.png and /dev/null differ diff --git a/examples/Screenshots/SynchronousConcurrency.png b/examples/Screenshots/SynchronousConcurrency.png deleted file mode 100644 index e0eac3788e..0000000000 Binary files a/examples/Screenshots/SynchronousConcurrency.png and /dev/null differ diff --git a/examples/Screenshots/VerticalWithinHorizontalScrolling.gif b/examples/Screenshots/VerticalWithinHorizontalScrolling.gif deleted file mode 100644 index c50474b19b..0000000000 Binary files a/examples/Screenshots/VerticalWithinHorizontalScrolling.gif and /dev/null differ diff --git a/examples/Screenshots/VideoTableView.png b/examples/Screenshots/VideoTableView.png deleted file mode 100644 index ddeefdc773..0000000000 Binary files a/examples/Screenshots/VideoTableView.png and /dev/null differ diff --git a/examples/Screenshots/Videos.gif b/examples/Screenshots/Videos.gif deleted file mode 100644 index 5e5c5f2ca9..0000000000 Binary files a/examples/Screenshots/Videos.gif and /dev/null differ diff --git a/examples/SocialAppLayout-Inverted/Sample/PostNode.m b/examples/SocialAppLayout-Inverted/Sample/PostNode.m index a40e231c05..6323e2df01 100644 --- a/examples/SocialAppLayout-Inverted/Sample/PostNode.m +++ b/examples/SocialAppLayout-Inverted/Sample/PostNode.m @@ -50,6 +50,8 @@ if (self) { _post = post; + self.selectionStyle = UITableViewCellSelectionStyleNone; + // Name node _nameNode = [[ASTextNode alloc] init]; _nameNode.attributedText = [[NSAttributedString alloc] initWithString:_post.name attributes:[TextStyles nameStyle]]; @@ -180,6 +182,10 @@ _optionsNode = [[ASImageNode alloc] init]; _optionsNode.image = [UIImage imageNamed:@"icon_more"]; [self addSubnode:_optionsNode]; + + for (ASDisplayNode *node in self.subnodes) { + node.layerBacked = YES; + } } return self; } diff --git a/examples/SocialAppLayout-Inverted/Sample/ViewController.m b/examples/SocialAppLayout-Inverted/Sample/ViewController.m index 6e73b9e801..6bc0d48a2d 100644 --- a/examples/SocialAppLayout-Inverted/Sample/ViewController.m +++ b/examples/SocialAppLayout-Inverted/Sample/ViewController.m @@ -149,18 +149,4 @@ return self.socialAppDataSource.count; } -- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - PostNode *postNode = (PostNode *)[_tableNode nodeForRowAtIndexPath:indexPath]; - Post *post = self.socialAppDataSource[indexPath.row]; - - BOOL shouldRasterize = postNode.shouldRasterizeDescendants; - shouldRasterize = !shouldRasterize; - postNode.shouldRasterizeDescendants = shouldRasterize; - - NSLog(@"%@ rasterization for %@'s post: %@", shouldRasterize ? @"Enabling" : @"Disabling", post.name, postNode); - - [tableNode deselectRowAtIndexPath:indexPath animated:YES]; -} - @end diff --git a/examples/SocialAppLayout/Sample/PostNode.m b/examples/SocialAppLayout/Sample/PostNode.m index a40e231c05..d1b4a9cba1 100644 --- a/examples/SocialAppLayout/Sample/PostNode.m +++ b/examples/SocialAppLayout/Sample/PostNode.m @@ -50,6 +50,8 @@ if (self) { _post = post; + self.selectionStyle = UITableViewCellSelectionStyleNone; + // Name node _nameNode = [[ASTextNode alloc] init]; _nameNode.attributedText = [[NSAttributedString alloc] initWithString:_post.name attributes:[TextStyles nameStyle]]; @@ -180,6 +182,10 @@ _optionsNode = [[ASImageNode alloc] init]; _optionsNode.image = [UIImage imageNamed:@"icon_more"]; [self addSubnode:_optionsNode]; + + for (ASDisplayNode *node in self.subnodes) { + node.layerBacked = YES; + } } return self; } diff --git a/examples/SocialAppLayout/Sample/ViewController.m b/examples/SocialAppLayout/Sample/ViewController.m index 71458a1ae8..28bdf90283 100644 --- a/examples/SocialAppLayout/Sample/ViewController.m +++ b/examples/SocialAppLayout/Sample/ViewController.m @@ -133,18 +133,4 @@ return self.socialAppDataSource.count; } -- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - PostNode *postNode = (PostNode *)[_tableNode nodeForRowAtIndexPath:indexPath]; - Post *post = self.socialAppDataSource[indexPath.row]; - - BOOL shouldRasterize = postNode.shouldRasterizeDescendants; - shouldRasterize = !shouldRasterize; - postNode.shouldRasterizeDescendants = shouldRasterize; - - NSLog(@"%@ rasterization for %@'s post: %@", shouldRasterize ? @"Enabling" : @"Disabling", post.name, postNode); - - [tableNode deselectRowAtIndexPath:indexPath animated:YES]; -} - @end diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m b/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m index 9d9f9b9780..4791a42f08 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m +++ b/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m @@ -39,7 +39,7 @@ _pagerNode = [[ASPagerNode alloc] init]; _pagerNode.dataSource = self; _pagerNode.delegate = self; - [ASRangeController setShouldShowRangeDebugOverlay:YES]; + ASDisplayNode.shouldShowRangeDebugOverlay = YES; // Could implement ASCollectionDelegate if we wanted extra callbacks, like from UIScrollView. //_pagerNode.delegate = self; diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.pbxproj b/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..5927a1d960 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.pbxproj @@ -0,0 +1,523 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3A2362FB1E2D33A0007E08F1 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2362FA1E2D33A0007E08F1 /* Date.swift */; }; + 3A7A28D91E2F7410003E2B8D /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7A28D81E2F7410003E2B8D /* UIImage.swift */; }; + 3AB33F5E1E1F94530039F711 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F5D1E1F94530039F711 /* AppDelegate.swift */; }; + 3AB33F651E1F94530039F711 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AB33F641E1F94530039F711 /* Assets.xcassets */; }; + 3AB33F681E1F94530039F711 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */; }; + 3AB33F761E1F9C330039F711 /* PhotoFeedTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */; }; + 3AB33F781E1F9C400039F711 /* PhotoFeedTableNodeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */; }; + 3AB33F7B1E1F9E630039F711 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F7A1E1F9E630039F711 /* UIColor.swift */; }; + 3AB33F811E1FDE100039F711 /* Webservice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F801E1FDE100039F711 /* Webservice.swift */; }; + 3AB33F831E20E81E0039F711 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F821E20E81E0039F711 /* Constants.swift */; }; + 3AB33F861E20E9B10039F711 /* PhotoFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */; }; + 3AB33F881E20ED460039F711 /* PhotoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F871E20ED460039F711 /* PhotoModel.swift */; }; + 3AB33F8C1E2106F30039F711 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F8B1E2106F30039F711 /* URL.swift */; }; + 3AB33F961E2269D40039F711 /* PopularPageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F951E2269D40039F711 /* PopularPageModel.swift */; }; + 3AB33F981E22A0080039F711 /* PX500Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F971E22A0080039F711 /* PX500Convenience.swift */; }; + 3AB33F9E1E22D9DB0039F711 /* PhotoTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */; }; + 3AB33FA21E230A160039F711 /* NetworkImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33FA11E230A160039F711 /* NetworkImageView.swift */; }; + 3AB33FA41E2337850039F711 /* PhotoTableNodeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */; }; + 7E438240D2C4026931D60594 /* Pods_ASDKgram_Swift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ASDKgram-Swift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift.debug.xcconfig"; sourceTree = ""; }; + 3A2362FA1E2D33A0007E08F1 /* Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; + 3A7A28D81E2F7410003E2B8D /* UIImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; + 3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ASDKgram-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3AB33F5D1E1F94530039F711 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 3AB33F641E1F94530039F711 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 3AB33F671E1F94530039F711 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 3AB33F691E1F94530039F711 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedTableViewController.swift; sourceTree = ""; }; + 3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedTableNodeController.swift; sourceTree = ""; }; + 3AB33F7A1E1F9E630039F711 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; + 3AB33F801E1FDE100039F711 /* Webservice.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Webservice.swift; sourceTree = ""; }; + 3AB33F821E20E81E0039F711 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoFeedModel.swift; sourceTree = ""; }; + 3AB33F871E20ED460039F711 /* PhotoModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoModel.swift; sourceTree = ""; }; + 3AB33F8B1E2106F30039F711 /* URL.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; + 3AB33F951E2269D40039F711 /* PopularPageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopularPageModel.swift; sourceTree = ""; }; + 3AB33F971E22A0080039F711 /* PX500Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PX500Convenience.swift; sourceTree = ""; }; + 3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoTableViewCell.swift; sourceTree = ""; }; + 3AB33FA11E230A160039F711 /* NetworkImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkImageView.swift; sourceTree = ""; }; + 3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoTableNodeCell.swift; sourceTree = ""; }; + 4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ASDKgram_Swift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ASDKgram-Swift.release.xcconfig"; path = "Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3AB33F571E1F94520039F711 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7E438240D2C4026931D60594 /* Pods_ASDKgram_Swift.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3AB33F511E1F94520039F711 = { + isa = PBXGroup; + children = ( + 3AB33F5C1E1F94530039F711 /* ASDKgram-Swift */, + 3AB33F5B1E1F94520039F711 /* Products */, + 78A64CA59A49BE1637214DF1 /* Pods */, + A7DD645D70CF34C7CA3B1A8B /* Frameworks */, + ); + sourceTree = ""; + }; + 3AB33F5B1E1F94520039F711 /* Products */ = { + isa = PBXGroup; + children = ( + 3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */, + ); + name = Products; + sourceTree = ""; + }; + 3AB33F5C1E1F94530039F711 /* ASDKgram-Swift */ = { + isa = PBXGroup; + children = ( + 3AB33F991E22CF160039F711 /* Views */, + 3AB33F841E20E98C0039F711 /* Model */, + 3AB33F7D1E1FDA890039F711 /* Client */, + 3AB33F791E1F9E4E0039F711 /* Extensions */, + 3AB33F721E1F9B650039F711 /* Controllers */, + 3AB33F5D1E1F94530039F711 /* AppDelegate.swift */, + 3AB33F821E20E81E0039F711 /* Constants.swift */, + 3AB33F641E1F94530039F711 /* Assets.xcassets */, + 3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */, + 3AB33F691E1F94530039F711 /* Info.plist */, + ); + path = "ASDKgram-Swift"; + sourceTree = ""; + }; + 3AB33F721E1F9B650039F711 /* Controllers */ = { + isa = PBXGroup; + children = ( + 3AB33F741E1F9B9F0039F711 /* ASDK */, + 3AB33F731E1F9B950039F711 /* UIKit */, + ); + name = Controllers; + sourceTree = ""; + }; + 3AB33F731E1F9B950039F711 /* UIKit */ = { + isa = PBXGroup; + children = ( + 3AB33F751E1F9C330039F711 /* PhotoFeedTableViewController.swift */, + ); + name = UIKit; + sourceTree = ""; + }; + 3AB33F741E1F9B9F0039F711 /* ASDK */ = { + isa = PBXGroup; + children = ( + 3AB33F771E1F9C400039F711 /* PhotoFeedTableNodeController.swift */, + ); + name = ASDK; + sourceTree = ""; + }; + 3AB33F791E1F9E4E0039F711 /* Extensions */ = { + isa = PBXGroup; + children = ( + 3AB33F7A1E1F9E630039F711 /* UIColor.swift */, + 3AB33F8B1E2106F30039F711 /* URL.swift */, + 3A2362FA1E2D33A0007E08F1 /* Date.swift */, + 3A7A28D81E2F7410003E2B8D /* UIImage.swift */, + ); + name = Extensions; + sourceTree = ""; + }; + 3AB33F7D1E1FDA890039F711 /* Client */ = { + isa = PBXGroup; + children = ( + 3AB33F801E1FDE100039F711 /* Webservice.swift */, + 3AB33F971E22A0080039F711 /* PX500Convenience.swift */, + ); + name = Client; + sourceTree = ""; + }; + 3AB33F841E20E98C0039F711 /* Model */ = { + isa = PBXGroup; + children = ( + 3AB33F851E20E9B10039F711 /* PhotoFeedModel.swift */, + 3AB33F871E20ED460039F711 /* PhotoModel.swift */, + 3AB33F951E2269D40039F711 /* PopularPageModel.swift */, + ); + name = Model; + sourceTree = ""; + }; + 3AB33F991E22CF160039F711 /* Views */ = { + isa = PBXGroup; + children = ( + 3AB33F9B1E22CF3C0039F711 /* UIKit */, + 3AB33F9C1E22CF5C0039F711 /* ASDK */, + ); + name = Views; + sourceTree = ""; + }; + 3AB33F9B1E22CF3C0039F711 /* UIKit */ = { + isa = PBXGroup; + children = ( + 3AB33F9D1E22D9DB0039F711 /* PhotoTableViewCell.swift */, + 3AB33FA11E230A160039F711 /* NetworkImageView.swift */, + ); + name = UIKit; + sourceTree = ""; + }; + 3AB33F9C1E22CF5C0039F711 /* ASDK */ = { + isa = PBXGroup; + children = ( + 3AB33FA31E2337850039F711 /* PhotoTableNodeCell.swift */, + ); + name = ASDK; + sourceTree = ""; + }; + 78A64CA59A49BE1637214DF1 /* Pods */ = { + isa = PBXGroup; + children = ( + 019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */, + A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + A7DD645D70CF34C7CA3B1A8B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4D7D664E4FF432C4AE232A56 /* Pods_ASDKgram_Swift.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 3AB33F591E1F94520039F711 /* ASDKgram-Swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3AB33F6C1E1F94530039F711 /* Build configuration list for PBXNativeTarget "ASDKgram-Swift" */; + buildPhases = ( + A5A729883237749EE5D2DB1C /* [CP] Check Pods Manifest.lock */, + 3AB33F561E1F94520039F711 /* Sources */, + 3AB33F571E1F94520039F711 /* Frameworks */, + 3AB33F581E1F94520039F711 /* Resources */, + 154783123A953C3AFB9805CF /* [CP] Embed Pods Frameworks */, + 07D25AC7E9C9518F14F0C929 /* [CP] Copy Pods Resources */, + 3A7BEDD71E254278005769D4 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ASDKgram-Swift"; + productName = "ASDKgram-Swift"; + productReference = 3AB33F5A1E1F94520039F711 /* ASDKgram-Swift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3AB33F521E1F94520039F711 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = "Calum Harris"; + TargetAttributes = { + 3AB33F591E1F94520039F711 = { + CreatedOnToolsVersion = 8.2; + DevelopmentTeam = B3H446T9U7; + LastSwiftMigration = 0820; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 3AB33F551E1F94520039F711 /* Build configuration list for PBXProject "ASDKgram-Swift" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3AB33F511E1F94520039F711; + productRefGroup = 3AB33F5B1E1F94520039F711 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3AB33F591E1F94520039F711 /* ASDKgram-Swift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3AB33F581E1F94520039F711 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3AB33F681E1F94530039F711 /* LaunchScreen.storyboard in Resources */, + 3AB33F651E1F94530039F711 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 07D25AC7E9C9518F14F0C929 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 154783123A953C3AFB9805CF /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ASDKgram-Swift/Pods-ASDKgram-Swift-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 3A7BEDD71E254278005769D4 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ""; + }; + A5A729883237749EE5D2DB1C /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3AB33F561E1F94520039F711 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3AB33F781E1F9C400039F711 /* PhotoFeedTableNodeController.swift in Sources */, + 3A2362FB1E2D33A0007E08F1 /* Date.swift in Sources */, + 3AB33F7B1E1F9E630039F711 /* UIColor.swift in Sources */, + 3AB33F981E22A0080039F711 /* PX500Convenience.swift in Sources */, + 3AB33FA41E2337850039F711 /* PhotoTableNodeCell.swift in Sources */, + 3AB33FA21E230A160039F711 /* NetworkImageView.swift in Sources */, + 3AB33F8C1E2106F30039F711 /* URL.swift in Sources */, + 3AB33F831E20E81E0039F711 /* Constants.swift in Sources */, + 3AB33F961E2269D40039F711 /* PopularPageModel.swift in Sources */, + 3A7A28D91E2F7410003E2B8D /* UIImage.swift in Sources */, + 3AB33F5E1E1F94530039F711 /* AppDelegate.swift in Sources */, + 3AB33F811E1FDE100039F711 /* Webservice.swift in Sources */, + 3AB33F9E1E22D9DB0039F711 /* PhotoTableViewCell.swift in Sources */, + 3AB33F861E20E9B10039F711 /* PhotoFeedModel.swift in Sources */, + 3AB33F881E20ED460039F711 /* PhotoModel.swift in Sources */, + 3AB33F761E1F9C330039F711 /* PhotoFeedTableViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 3AB33F661E1F94530039F711 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 3AB33F671E1F94530039F711 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 3AB33F6A1E1F94530039F711 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 3AB33F6B1E1F94530039F711 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3AB33F6D1E1F94530039F711 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 019E984FADA258377FC6B2D8 /* Pods-ASDKgram-Swift.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = B3H446T9U7; + INFOPLIST_FILE = "ASDKgram-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.RenaldoMoon.ASDKgram-Swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 3AB33F6E1E1F94530039F711 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A3A86E74A8C3F06D7688AACB /* Pods-ASDKgram-Swift.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + DEVELOPMENT_TEAM = B3H446T9U7; + INFOPLIST_FILE = "ASDKgram-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.RenaldoMoon.ASDKgram-Swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3AB33F551E1F94520039F711 /* Build configuration list for PBXProject "ASDKgram-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3AB33F6A1E1F94530039F711 /* Debug */, + 3AB33F6B1E1F94530039F711 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3AB33F6C1E1F94530039F711 /* Build configuration list for PBXNativeTarget "ASDKgram-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3AB33F6D1E1F94530039F711 /* Debug */, + 3AB33F6E1E1F94530039F711 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3AB33F521E1F94520039F711 /* Project object */; +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..96a0fb6dec --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/AppDelegate.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/AppDelegate.swift new file mode 100644 index 0000000000..886d28ca70 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/AppDelegate.swift @@ -0,0 +1,61 @@ +// +// AppDelegate.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 06/01/2017. +// +// 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 +import AsyncDisplayKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + + // UIKit Home Feed viewController & navController + + let UIKitNavController = UINavigationController(rootViewController: PhotoFeedTableViewController()) + UIKitNavController.tabBarItem.title = "UIKit" + + // ASDK Home Feed viewController & navController + + let ASDKNavController = UINavigationController(rootViewController: PhotoFeedTableNodeController()) + ASDKNavController.tabBarItem.title = "ASDK" + + // UITabBarController + + let tabBarController = UITabBarController() + tabBarController.viewControllers = [UIKitNavController, ASDKNavController] + tabBarController.selectedIndex = 1 + tabBarController.tabBar.tintColor = UIColor.mainBarTintColor() + + // Nav Bar appearance + + UINavigationBar.appearance().barTintColor = UIColor.mainBarTintColor() + + // UIWindow + + window = UIWindow() + window?.backgroundColor = .white + window?.rootViewController = tabBarController + window?.makeKeyAndVisible() + + return true + } + +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..1d060ed288 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/LaunchScreen.storyboard b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..c1191aaf8d --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/Main.storyboard b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..273375fc70 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Constants.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Constants.swift new file mode 100644 index 0000000000..f85bb817fe --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Constants.swift @@ -0,0 +1,45 @@ +// +// Constants +// ASDKgram-Swift +// +// Created by Calum Harris on 07/01/2017. +// +// 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. +// +// swiftlint:disable nesting + +import UIKit + +struct Constants { + + struct PX500 { + struct URLS { + static let Host = "https://api.500px.com/v1/" + static let PopularEndpoint = "photos?feature=popular&exclude=Nude,People,Fashion&sort=rating&image_size=3&include_store=store_download&include_states=voted" + static let SearchEndpoint = "photos/search?geo=" //latitude,longitude,radius + static let UserEndpoint = "photos?user_id=" + static let ConsumerKey = "&consumer_key=Fi13GVb8g53sGvHICzlram7QkKOlSDmAmp9s9aqC" + } + } + + struct CellLayout { + static let FontSize: CGFloat = 14 + static let HeaderHeight: CGFloat = 50 + static let UserImageHeight: CGFloat = 30 + static let HorizontalBuffer: CGFloat = 10 + static let VerticalBuffer: CGFloat = 5 + static let InsetForAvatar = UIEdgeInsets(top: HorizontalBuffer, left: 0, bottom: HorizontalBuffer, right: HorizontalBuffer) + static let InsetForHeader = UIEdgeInsets(top: 0, left: HorizontalBuffer, bottom: 0, right: HorizontalBuffer) + static let InsetForFooter = UIEdgeInsets(top: VerticalBuffer, left: HorizontalBuffer, bottom: VerticalBuffer, right: HorizontalBuffer) + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Date.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Date.swift new file mode 100644 index 0000000000..0d7cde2d3d --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Date.swift @@ -0,0 +1,32 @@ +// +// Date.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 16/01/2017. +// +// 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 Foundation + +extension Date { + + static let iso8601Formatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .iso8601) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" + return formatter + }() +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Info.plist b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Info.plist new file mode 100644 index 0000000000..c12df3b8a9 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/NetworkImageView.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/NetworkImageView.swift new file mode 100644 index 0000000000..aeb860d8aa --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/NetworkImageView.swift @@ -0,0 +1,57 @@ +// +// NetworkImageView.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 09/01/2017. +// +// 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 + +let imageCache = NSCache() + +class NetworkImageView: UIImageView { + + var imageUrlString: String? + + func loadImageUsingUrlString(urlString: String) { + + imageUrlString = urlString + + let url = URL(string: urlString) + + image = nil + + if let imageFromCache = imageCache.object(forKey: urlString as NSString) { + self.image = imageFromCache + return + } + + URLSession.shared.dataTask(with: url!, completionHandler: { (data, respones, error) in + + if error != nil { + print(error!) + return + } + + DispatchQueue.main.async { + let imageToCache = UIImage(data: data!) + if self.imageUrlString == urlString { + self.image = imageToCache + } + imageCache.setObject(imageToCache!, forKey: urlString as NSString) + } + }).resume() + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PX500Convenience.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PX500Convenience.swift new file mode 100644 index 0000000000..808a553d14 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PX500Convenience.swift @@ -0,0 +1,34 @@ +// +// PX500Convenience.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 08/01/2017. +// +// 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 Foundation + +func parsePopularPage(withURL: URL) -> Resource { + + let parse = Resource(url: withURL, parseJSON: { jsonData in + + guard let json = jsonData as? JSONDictionary, let photos = json["photos"] as? [JSONDictionary] else { return .failure(.errorParsingJSON) } + + guard let model = PopularPageModel(dictionary: json, photosArray: photos.flatMap(PhotoModel.init)) else { return .failure(.errorParsingJSON) } + + return .success(model) + }) + + return parse +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift new file mode 100644 index 0000000000..9a7cd52020 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedModel.swift @@ -0,0 +1,124 @@ +// +// PhotoFeedModel.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 07/01/2017. +// +// 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 + +final class PhotoFeedModel { + + public private(set) var photoFeedModelType: PhotoFeedModelType + public private(set) var photos: [PhotoModel] = [] + public private(set) var imageSize: CGSize + private var url: URL + private var ids: [Int] = [] + private var currentPage: Int = 0 + private var totalPages: Int = 0 + public private(set) var totalItems: Int = 0 + private var fetchPageInProgress: Bool = false + private var refreshFeedInProgress: Bool = false + + init(initWithPhotoFeedModelType: PhotoFeedModelType, requiredImageSize: CGSize) { + self.photoFeedModelType = initWithPhotoFeedModelType + self.imageSize = requiredImageSize + self.url = URL.URLForFeedModelType(feedModelType: initWithPhotoFeedModelType) + } + + var numberOfItemsInFeed: Int { + return photos.count + } + + // return in completion handler the number of additions and the status of internet connection + + func updateNewBatchOfPopularPhotos(additionsAndConnectionStatusCompletion: @escaping (Int, InternetStatus) -> ()) { + + guard !fetchPageInProgress else { return } + + fetchPageInProgress = true + fetchNextPageOfPopularPhotos(replaceData: false) { [unowned self] additions, errors in + self.fetchPageInProgress = false + + if let error = errors { + switch error { + case .noInternetConnection: + additionsAndConnectionStatusCompletion(0, .noConnection) + default: additionsAndConnectionStatusCompletion(0, .connected) + } + } else { + additionsAndConnectionStatusCompletion(additions, .connected) + } + } + } + + private func fetchNextPageOfPopularPhotos(replaceData: Bool, numberOfAdditionsCompletion: @escaping (Int, NetworkingErrors?) -> ()) { + + if currentPage == totalPages, currentPage != 0 { + return numberOfAdditionsCompletion(0, .customError("No pages left to parse")) + } + + var newPhotos: [PhotoModel] = [] + var newIDs: [Int] = [] + + let pageToFetch = currentPage + 1 + + let url = self.url.addImageParameterForClosestImageSizeAndpage(size: imageSize, page: pageToFetch) + + WebService().load(resource: parsePopularPage(withURL: url)) { [unowned self] result in + + switch result { + case .success(let popularPage): + self.totalItems = popularPage.totalNumberOfItems + self.totalPages = popularPage.totalPages + self.currentPage = popularPage.page + + for photo in popularPage.photos { + if !replaceData || !self.ids.contains(photo.photoID) { + newPhotos.append(photo) + newIDs.append(photo.photoID) + } + } + + DispatchQueue.main.async { + if replaceData { + self.photos = newPhotos + self.ids = newIDs + } else { + self.photos += newPhotos + self.ids += newIDs + } + + numberOfAdditionsCompletion(newPhotos.count, nil) + } + + case .failure(let fail): + print(fail) + numberOfAdditionsCompletion(0, fail) + } + } + } +} + +enum PhotoFeedModelType { + case photoFeedModelTypePopular + case photoFeedModelTypeLocation + case photoFeedModelTypeUserPhotos +} + +enum InternetStatus { + case connected + case noConnection +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableNodeController.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableNodeController.swift new file mode 100644 index 0000000000..e8a0dce3ce --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableNodeController.swift @@ -0,0 +1,112 @@ +// +// PhotoFeedTableNodeController.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 06/01/2017. +// +// 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 AsyncDisplayKit + +class PhotoFeedTableNodeController: ASViewController { + + var activityIndicator: UIActivityIndicatorView! + var photoFeed: PhotoFeedModel + + init() { + photoFeed = PhotoFeedModel(initWithPhotoFeedModelType: .photoFeedModelTypePopular, requiredImageSize: screenSizeForWidth) + super.init(node: ASTableNode()) + self.navigationItem.title = "ASDK" + + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupActivityIndicator() + node.allowsSelection = false + node.view.separatorStyle = .none + node.dataSource = self + node.delegate = self + node.view.leadingScreensForBatching = 2.5 + navigationController?.hidesBarsOnSwipe = true + } + + // helper functions + func setupActivityIndicator() { + let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) + self.activityIndicator = activityIndicator + let bounds = self.node.frame + var refreshRect = activityIndicator.frame + refreshRect.origin = CGPoint(x: (bounds.size.width - activityIndicator.frame.size.width) / 2.0, y: (bounds.size.height - activityIndicator.frame.size.height) / 2.0) + activityIndicator.frame = refreshRect + self.node.view.addSubview(activityIndicator) + } + + var screenSizeForWidth: CGSize = { + let screenRect = UIScreen.main.bounds + let screenScale = UIScreen.main.scale + return CGSize(width: screenRect.size.width * screenScale, height: screenRect.size.width * screenScale) + }() + + func fetchNewBatchWithContext(_ context: ASBatchContext?) { + activityIndicator.startAnimating() + photoFeed.updateNewBatchOfPopularPhotos() { additions, connectionStatus in + switch connectionStatus { + case .connected: + self.activityIndicator.stopAnimating() + self.addRowsIntoTableNode(newPhotoCount: additions) + context?.completeBatchFetching(true) + case .noConnection: + self.activityIndicator.stopAnimating() + if context != nil { + context!.completeBatchFetching(true) + } + break + } + } + } + + func addRowsIntoTableNode(newPhotoCount newPhotos: Int) { + let indexRange = (photoFeed.photos.count - newPhotos.. Int { + return photoFeed.numberOfItemsInFeed + } + + func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) -> ASCellNodeBlock { + let photo = photoFeed.photos[indexPath.row] + let nodeBlock: ASCellNodeBlock = { _ in + return PhotoTableNodeCell(photoModel: photo) + } + return nodeBlock + } + + func shouldBatchFetchForCollectionNode(collectionNode: ASCollectionNode) -> Bool { + return true + } + + func tableNode(_ tableNode: ASTableNode, willBeginBatchFetchWith context: ASBatchContext) { + fetchNewBatchWithContext(context) + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableViewController.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableViewController.swift new file mode 100644 index 0000000000..0e7cbef975 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoFeedTableViewController.swift @@ -0,0 +1,121 @@ +// +// PhotoFeedTableViewController.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 06/01/2017. +// +// 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 + +class PhotoFeedTableViewController: UITableViewController { + + var activityIndicator: UIActivityIndicatorView! + var photoFeed: PhotoFeedModel + + init() { + photoFeed = PhotoFeedModel(initWithPhotoFeedModelType: .photoFeedModelTypePopular, requiredImageSize: screenSizeForWidth) + super.init(nibName: nil, bundle: nil) + self.navigationItem.title = "UIKit" + + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + setupActivityIndicator() + configureTableView() + fetchNewBatch() + navigationController?.hidesBarsOnSwipe = true + } + + func fetchNewBatch() { + activityIndicator.startAnimating() + photoFeed.updateNewBatchOfPopularPhotos() { additions, connectionStatus in + switch connectionStatus { + case .connected: + self.activityIndicator.stopAnimating() + self.addRowsIntoTableView(newPhotoCount: additions) + case .noConnection: + self.activityIndicator.stopAnimating() + break + } + } + } + + var screenSizeForWidth: CGSize = { + let screenRect = UIScreen.main.bounds + let screenScale = UIScreen.main.scale + return CGSize(width: screenRect.size.width * screenScale, height: screenRect.size.width * screenScale) + }() + + // helper functions + func setupActivityIndicator() { + let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) + self.activityIndicator = activityIndicator + self.tableView.addSubview(activityIndicator) + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + activityIndicator.centerXAnchor.constraint(equalTo: self.tableView.centerXAnchor), + activityIndicator.centerYAnchor.constraint(equalTo: self.tableView.centerYAnchor) + ]) + } + + func configureTableView() { + tableView.register(PhotoTableViewCell.self, forCellReuseIdentifier: "photoCell") + tableView.allowsSelection = false + tableView.rowHeight = UITableViewAutomaticDimension + tableView.separatorStyle = .none + } +} + +extension PhotoFeedTableViewController { + + func addRowsIntoTableView(newPhotoCount newPhotos: Int) { + + let indexRange = (photoFeed.photos.count - newPhotos.. Int { + return photoFeed.photos.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "photoCell", for: indexPath) as? PhotoTableViewCell else { fatalError("Wrong cell type") } + cell.photoModel = photoFeed.photos[indexPath.row] + return cell + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return PhotoTableViewCell.height(for: photoFeed.photos[indexPath.row], withWidth: self.view.frame.size.width) + } + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + + let currentOffSetY = scrollView.contentOffset.y + let contentHeight = scrollView.contentSize.height + let screenHeight = UIScreen.main.bounds.size.height + let screenfullsBeforeBottom = (contentHeight - currentOffSetY) / screenHeight + if screenfullsBeforeBottom < 2.5 { + self.fetchNewBatch() + } + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoModel.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoModel.swift new file mode 100644 index 0000000000..e4070bb257 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoModel.swift @@ -0,0 +1,116 @@ +// +// PhotoModel.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 07/01/2017. +// +// 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 + +typealias JSONDictionary = [String : Any] + +struct PhotoModel { + + let url: String + let photoID: Int + let dateString: String + let descriptionText: String + let likesCount: Int + let ownerUserName: String + let ownerPicURL: String + + init?(dictionary: JSONDictionary) { + + guard let url = dictionary["image_url"] as? String, let date = dictionary["created_at"] as? String, let photoID = dictionary["id"] as? Int, let descriptionText = dictionary["name"] as? String, let likesCount = dictionary["positive_votes_count"] as? Int else { print("error parsing JSON within PhotoModel Init"); return nil } + + guard let user = dictionary["user"] as? JSONDictionary, let username = user["username"] as? String, let ownerPicURL = user["userpic_url"] as? String else { print("error parsing JSON within PhotoModel Init"); return nil } + + self.url = url + self.photoID = photoID + self.descriptionText = descriptionText + self.likesCount = likesCount + self.dateString = date + self.ownerUserName = username + self.ownerPicURL = ownerPicURL + } +} + +extension PhotoModel { + + // MARK: - Attributed Strings + + func attrStringForUserName(withSize size: CGFloat) -> NSAttributedString { + let attr = [ + NSForegroundColorAttributeName : UIColor.darkGray, + NSFontAttributeName: UIFont.boldSystemFont(ofSize: size) + ] + return NSAttributedString(string: self.ownerUserName, attributes: attr) + } + + func attrStringForDescription(withSize size: CGFloat) -> NSAttributedString { + let attr = [ + NSForegroundColorAttributeName : UIColor.darkGray, + NSFontAttributeName: UIFont.systemFont(ofSize: size) + ] + return NSAttributedString(string: self.descriptionText, attributes: attr) + } + + func attrStringLikes(withSize size: CGFloat) -> NSAttributedString { + + let formatter = NumberFormatter() + formatter.numberStyle = .decimal + let formattedLikesNumber: String? = formatter.string(from: NSNumber(value: self.likesCount)) + let likesString: String = "\(formattedLikesNumber!) Likes" + let textAttr = [NSForegroundColorAttributeName : UIColor.mainBarTintColor(), NSFontAttributeName: UIFont.systemFont(ofSize: size)] + let likesAttrString = NSAttributedString(string: likesString, attributes: textAttr) + + let heartAttr = [NSForegroundColorAttributeName : UIColor.red, NSFontAttributeName: UIFont.systemFont(ofSize: size)] + let heartAttrString = NSAttributedString(string: "♥︎ ", attributes: heartAttr) + + let combine = NSMutableAttributedString() + combine.append(heartAttrString) + combine.append(likesAttrString) + return combine + } + + func attrStringForTimeSinceString(withSize size: CGFloat) -> NSAttributedString { + + let attr = [ + NSForegroundColorAttributeName : UIColor.mainBarTintColor(), + NSFontAttributeName: UIFont.systemFont(ofSize: size) + ] + + let date = Date.iso8601Formatter.date(from: self.dateString)! + return NSAttributedString(string: timeStringSince(fromConverted: date), attributes: attr) + } + + private func timeStringSince(fromConverted date: Date) -> String { + let diffDates = NSCalendar.current.dateComponents([.day, .hour, .second], from: date, to: Date()) + + if let week = diffDates.day, week > 7 { + return "\(week / 7)w" + } else if let day = diffDates.day, day > 0 { + return "\(day)d" + } else if let hour = diffDates.hour, hour > 0 { + return "\(hour)h" + } else if let second = diffDates.second, second > 0 { + return "\(second)s" + } else if let zero = diffDates.second, zero == 0 { + return "1s" + } else { + return "ERROR" + } + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableNodeCell.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableNodeCell.swift new file mode 100644 index 0000000000..56e02588b5 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableNodeCell.swift @@ -0,0 +1,84 @@ +// +// PhotoTableNodeCell.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 09/01/2017. +// +// 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 Foundation +import AsyncDisplayKit + +class PhotoTableNodeCell: ASCellNode { + + let usernameLabel = ASTextNode() + let timeIntervalLabel = ASTextNode() + let photoLikesLabel = ASTextNode() + let photoDescriptionLabel = ASTextNode() + + let avatarImageNode: ASNetworkImageNode = { + let imageNode = ASNetworkImageNode() + imageNode.contentMode = .scaleAspectFill + imageNode.imageModificationBlock = ASImageNodeRoundBorderModificationBlock(0, nil) + return imageNode + }() + + let photoImageNode: ASNetworkImageNode = { + let imageNode = ASNetworkImageNode() + imageNode.contentMode = .scaleAspectFill + return imageNode + }() + + init(photoModel: PhotoModel) { + super.init() + self.photoImageNode.url = URL(string: photoModel.url) + self.avatarImageNode.url = URL(string: photoModel.ownerPicURL) + self.usernameLabel.attributedText = photoModel.attrStringForUserName(withSize: Constants.CellLayout.FontSize) + self.timeIntervalLabel.attributedText = photoModel.attrStringForTimeSinceString(withSize: Constants.CellLayout.FontSize) + self.photoLikesLabel.attributedText = photoModel.attrStringLikes(withSize: Constants.CellLayout.FontSize) + self.photoDescriptionLabel.attributedText = photoModel.attrStringForDescription(withSize: Constants.CellLayout.FontSize) + self.automaticallyManagesSubnodes = true + } + + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + + // Header Stack + + var headerChildren: [ASLayoutElement] = [] + + let headerStack = ASStackLayoutSpec.horizontal() + headerStack.alignItems = .center + avatarImageNode.style.preferredSize = CGSize(width: Constants.CellLayout.UserImageHeight, height: Constants.CellLayout.UserImageHeight) + headerChildren.append(ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForAvatar, child: avatarImageNode)) + usernameLabel.style.flexShrink = 1.0 + headerChildren.append(usernameLabel) + + let spacer = ASLayoutSpec() + spacer.style.flexGrow = 1.0 + headerChildren.append(spacer) + + timeIntervalLabel.style.spacingBefore = Constants.CellLayout.HorizontalBuffer + headerChildren.append(timeIntervalLabel) + + let footerStack = ASStackLayoutSpec.vertical() + footerStack.spacing = Constants.CellLayout.VerticalBuffer + footerStack.children = [photoLikesLabel, photoDescriptionLabel] + headerStack.children = headerChildren + + let verticalStack = ASStackLayoutSpec.vertical() + + verticalStack.children = [ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForHeader, child: headerStack), ASRatioLayoutSpec(ratio: 1.0, child: photoImageNode), ASInsetLayoutSpec(insets: Constants.CellLayout.InsetForFooter, child: footerStack)] + + return verticalStack + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableViewCell.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableViewCell.swift new file mode 100644 index 0000000000..b57beab9a0 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PhotoTableViewCell.swift @@ -0,0 +1,142 @@ +// +// PhotoTableViewCell.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 08/01/2017. +// +// 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 + +class PhotoTableViewCell: UITableViewCell { + + var photoModel: PhotoModel? { + didSet { + if let model = photoModel { + photoImageView.loadImageUsingUrlString(urlString: model.url) + avatarImageView.loadImageUsingUrlString(urlString: model.ownerPicURL) + photoLikesLabel.attributedText = model.attrStringLikes(withSize: Constants.CellLayout.FontSize) + usernameLabel.attributedText = model.attrStringForUserName(withSize: Constants.CellLayout.FontSize) + timeIntervalLabel.attributedText = model.attrStringForTimeSinceString(withSize: Constants.CellLayout.FontSize) + photoDescriptionLabel.attributedText = model.attrStringForDescription(withSize: Constants.CellLayout.FontSize) + photoDescriptionLabel.sizeToFit() + var rect = photoDescriptionLabel.frame + let availableWidth = self.bounds.size.width - Constants.CellLayout.HorizontalBuffer * 2 + rect.size = model.attrStringForDescription(withSize: Constants.CellLayout.FontSize).boundingRect(with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size + photoDescriptionLabel.frame = rect + } + } + } + + let photoImageView: NetworkImageView = { + let imageView = NetworkImageView() + imageView.contentMode = .scaleAspectFill + imageView.translatesAutoresizingMaskIntoConstraints = false + return imageView + }() + + let avatarImageView: NetworkImageView = { + let imageView = NetworkImageView() + imageView.contentMode = .scaleAspectFill + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.layer.cornerRadius = Constants.CellLayout.UserImageHeight / 2 + imageView.clipsToBounds = true + return imageView + }() + + let usernameLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + let timeIntervalLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + let photoLikesLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + let photoDescriptionLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.numberOfLines = 3 + return label + }() + + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupViews() + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupViews() { + addSubview(photoImageView) + addSubview(avatarImageView) + addSubview(usernameLabel) + addSubview(timeIntervalLabel) + addSubview(photoLikesLabel) + addSubview(photoDescriptionLabel) + setupConstraints() + } + + func setupConstraints() { + + NSLayoutConstraint.activate ([ + //photoImageView + photoImageView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.CellLayout.HeaderHeight), + photoImageView.widthAnchor.constraint(equalTo: widthAnchor), + photoImageView.heightAnchor.constraint(equalTo: photoImageView.widthAnchor), + // avatarImageView + avatarImageView.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer), + avatarImageView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.CellLayout.HorizontalBuffer), + avatarImageView.heightAnchor.constraint(equalToConstant: Constants.CellLayout.UserImageHeight), + avatarImageView.widthAnchor.constraint(equalTo: avatarImageView.heightAnchor), + // usernameLabel + usernameLabel.leftAnchor.constraint(equalTo: avatarImageView.rightAnchor, constant: Constants.CellLayout.HorizontalBuffer), + usernameLabel.rightAnchor.constraint(equalTo: timeIntervalLabel.leftAnchor, constant: -Constants.CellLayout.HorizontalBuffer), + usernameLabel.centerYAnchor.constraint(equalTo: avatarImageView.centerYAnchor), + // timeIntervalLabel + timeIntervalLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -Constants.CellLayout.HorizontalBuffer), + timeIntervalLabel.centerYAnchor.constraint(equalTo: avatarImageView.centerYAnchor), + // photoLikesLabel + photoLikesLabel.topAnchor.constraint(equalTo: photoImageView.bottomAnchor, constant: Constants.CellLayout.VerticalBuffer), + photoLikesLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer), + // photoDescriptionLabel + photoDescriptionLabel.topAnchor.constraint(equalTo: photoLikesLabel.bottomAnchor, constant: Constants.CellLayout.VerticalBuffer), + photoDescriptionLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: Constants.CellLayout.HorizontalBuffer), + photoDescriptionLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: -Constants.CellLayout.HorizontalBuffer), + photoDescriptionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.CellLayout.VerticalBuffer) + ]) + } + + class func height(for photo: PhotoModel, withWidth width: CGFloat) -> CGFloat { + let photoHeight = width + let font = UIFont.systemFont(ofSize: Constants.CellLayout.FontSize) + let likesHeight = round(font.lineHeight) + let descriptionAttrString = photo.attrStringForDescription(withSize: Constants.CellLayout.FontSize) + let availableWidth = width - Constants.CellLayout.HorizontalBuffer * 2 + let descriptionHeight = descriptionAttrString.boundingRect(with: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height + + return likesHeight + descriptionHeight + photoHeight + Constants.CellLayout.HeaderHeight + Constants.CellLayout.VerticalBuffer * 3 + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PopularPageModel.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PopularPageModel.swift new file mode 100644 index 0000000000..8aca2445f1 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/PopularPageModel.swift @@ -0,0 +1,37 @@ +// +// PopularPageModel.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 08/01/2017. +// +// 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 Foundation + +class PopularPageModel: NSObject { + + let page: Int + let totalPages: Int + let totalNumberOfItems: Int + let photos: [PhotoModel] + + init?(dictionary: JSONDictionary, photosArray: [PhotoModel]) { + guard let page = dictionary["current_page"] as? Int, let totalPages = dictionary["total_pages"] as? Int, let totalItems = dictionary["total_items"] as? Int else { print("error parsing JSON within PhotoModel Init"); return nil } + + self.page = page + self.totalPages = totalPages + self.totalNumberOfItems = totalItems + self.photos = photosArray + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIColor.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIColor.swift new file mode 100644 index 0000000000..5ab9d7e010 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIColor.swift @@ -0,0 +1,26 @@ +// +// UIColor.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 06/01/2017. +// 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 + +extension UIColor { + + class func mainBarTintColor() -> UIColor { + return UIColor(red: 69/255, green: 142/255, blue: 255/255, alpha: 1) + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIImage.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIImage.swift new file mode 100644 index 0000000000..4eb233f37b --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/UIImage.swift @@ -0,0 +1,60 @@ +// +// UIImage.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 18/01/2017. +// +// 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 + +// This extension was copied directly from LayoutSpecExamples-Swift. It is an example of how to create Precomoposed Alpha Corners. I have used the helper ASImageNodeRoundBorderModificationBlock:boarderWidth:boarderColor function in practice which does the same. + +extension UIImage { + + func makeCircularImage(size: CGSize, borderWidth width: CGFloat) -> UIImage { + // make a CGRect with the image's size + let circleRect = CGRect(origin: .zero, size: size) + + // begin the image context since we're not in a drawRect: + UIGraphicsBeginImageContextWithOptions(circleRect.size, false, 0) + + // create a UIBezierPath circle + let circle = UIBezierPath(roundedRect: circleRect, cornerRadius: circleRect.size.width * 0.5) + + // clip to the circle + circle.addClip() + + UIColor.white.set() + circle.fill() + + // draw the image in the circleRect *AFTER* the context is clipped + self.draw(in: circleRect) + + // create a border (for white background pictures) + if width > 0 { + circle.lineWidth = width + UIColor.white.set() + circle.stroke() + } + + // get an image from the image context + let roundedImage = UIGraphicsGetImageFromCurrentImageContext() + + // end the image context since we're not in a drawRect: + UIGraphicsEndImageContext() + + return roundedImage ?? self + } +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/URL.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/URL.swift new file mode 100644 index 0000000000..5960498155 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/URL.swift @@ -0,0 +1,67 @@ +// +// URL.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 07/01/2017. +// +// 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 + +extension URL { + + static func URLForFeedModelType(feedModelType: PhotoFeedModelType) -> URL { + switch feedModelType { + case .photoFeedModelTypePopular: + return URL(string: assemble500PXURLString(endpoint: Constants.PX500.URLS.PopularEndpoint))! + + case .photoFeedModelTypeLocation: + return URL(string: assemble500PXURLString(endpoint: Constants.PX500.URLS.SearchEndpoint))! + + case .photoFeedModelTypeUserPhotos: + return URL(string: assemble500PXURLString(endpoint: Constants.PX500.URLS.UserEndpoint))! + } + } + + private static func assemble500PXURLString(endpoint: String) -> String { + return Constants.PX500.URLS.Host + endpoint + Constants.PX500.URLS.ConsumerKey + } + + mutating func addImageParameterForClosestImageSizeAndpage(size: CGSize, page: Int) -> URL { + + let imageParameterID: Int + + if size.height <= 70 { + imageParameterID = 1 + } else if size.height <= 100 { + imageParameterID = 100 + } else if size.height <= 140 { + imageParameterID = 2 + } else if size.height <= 200 { + imageParameterID = 200 + } else if size.height <= 280 { + imageParameterID = 3 + } else if size.height <= 400 { + imageParameterID = 400 + } else { + imageParameterID = 600 + } + + var urlString = self.absoluteString + urlString.append("&image_size=\(imageParameterID)&page=\(page)") + + return URL(string: urlString)! + } + +} diff --git a/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Webservice.swift b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Webservice.swift new file mode 100644 index 0000000000..e2196208d2 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/ASDKgram-Swift/Webservice.swift @@ -0,0 +1,93 @@ +// +// Webservice.swift +// ASDKgram-Swift +// +// Created by Calum Harris on 06/01/2017. +// +// 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. +// +// swiftlint:disable force_cast + +import UIKit + +final class WebService { + func load(resource: Resource, completion: @escaping (Result) -> ()) { + URLSession.shared.dataTask(with: resource.url) { data, response, error in + // Check for errors in responses. + let result = self.checkForNetworkErrors(data, response, error) + + switch result { + case .success(let data): + completion(resource.parse(data)) + case .failure(let error): + completion(.failure(error)) + } + }.resume() + } +} + +extension WebService { + + fileprivate func checkForNetworkErrors(_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Result { + // Check for errors in responses. + guard error == nil else { + if (error as! NSError).domain == NSURLErrorDomain && ((error as! NSError).code == NSURLErrorNotConnectedToInternet || (error as! NSError).code == NSURLErrorTimedOut) { + return .failure(.noInternetConnection) + } else { + return .failure(.returnedError(error!)) + } + } + + guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else { + return .failure((.invalidStatusCode("Request returned status code other than 2xx \(response)"))) + } + + guard let data = data else { return .failure(.dataReturnedNil) } + + return .success(data) + } +} + +struct Resource { + let url: URL + let parse: (Data) -> Result +} + +extension Resource { + + init(url: URL, parseJSON: @escaping (Any) -> Result) { + self.url = url + self.parse = { data in + do { + let jsonData = try JSONSerialization.jsonObject(with: data, options: []) + return parseJSON(jsonData) + } catch { + fatalError("Error parsing data") + } + } + } +} + +enum Result { + case success(T) + case failure(NetworkingErrors) +} + +enum NetworkingErrors: Error { + case errorParsingJSON + case noInternetConnection + case dataReturnedNil + case returnedError(Error) + case invalidStatusCode(String) + case customError(String) +} diff --git a/examples_extra/ASDKgram-Swift/Podfile b/examples_extra/ASDKgram-Swift/Podfile new file mode 100644 index 0000000000..9e67970879 --- /dev/null +++ b/examples_extra/ASDKgram-Swift/Podfile @@ -0,0 +1,8 @@ + +target 'ASDKgram-Swift' do + + use_frameworks! + + pod 'AsyncDisplayKit', '>= 2.0' + +end diff --git a/examples_extra/RepoSearcher/Podfile b/examples_extra/RepoSearcher/Podfile new file mode 100644 index 0000000000..2652a3aca1 --- /dev/null +++ b/examples_extra/RepoSearcher/Podfile @@ -0,0 +1,10 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'RepoSearcher' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for RepoSearcher + pod 'AsyncDisplayKit/IGListKit', :path => '../..' +end diff --git a/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..2dac7da9fe --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.pbxproj @@ -0,0 +1,422 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 427F7FCA1E58519300D3E11B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FC91E58519300D3E11B /* AppDelegate.swift */; }; + 427F7FCC1E58519300D3E11B /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FCB1E58519300D3E11B /* SearchViewController.swift */; }; + 427F7FD11E58519300D3E11B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 427F7FD01E58519300D3E11B /* Assets.xcassets */; }; + 427F7FDC1E58558C00D3E11B /* LabelSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FDB1E58558C00D3E11B /* LabelSectionController.swift */; }; + 427F7FDE1E58626A00D3E11B /* SearchSectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FDD1E58626A00D3E11B /* SearchSectionController.swift */; }; + 427F7FE01E58627B00D3E11B /* SearchNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FDF1E58627B00D3E11B /* SearchNode.swift */; }; + 427F7FE21E58659600D3E11B /* NSObject+IGListDiffable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FE11E58659600D3E11B /* NSObject+IGListDiffable.swift */; }; + 427F7FE71E5868BD00D3E11B /* IGListCollectionContext+ASDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 427F7FE61E5868BD00D3E11B /* IGListCollectionContext+ASDK.swift */; }; + 427F7FED1E5872D200D3E11B /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 427F7FEC1E5872D200D3E11B /* Launch Screen.storyboard */; }; + E222079F2736F3FCAE57814E /* Pods_RepoSearcher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD4426BE878430E4E8B66198 /* Pods_RepoSearcher.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 427F7FC61E58519300D3E11B /* RepoSearcher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RepoSearcher.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 427F7FC91E58519300D3E11B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 427F7FCB1E58519300D3E11B /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = ""; }; + 427F7FD01E58519300D3E11B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 427F7FD51E58519300D3E11B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 427F7FDB1E58558C00D3E11B /* LabelSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelSectionController.swift; sourceTree = ""; }; + 427F7FDD1E58626A00D3E11B /* SearchSectionController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchSectionController.swift; sourceTree = ""; }; + 427F7FDF1E58627B00D3E11B /* SearchNode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchNode.swift; sourceTree = ""; }; + 427F7FE11E58659600D3E11B /* NSObject+IGListDiffable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+IGListDiffable.swift"; sourceTree = ""; }; + 427F7FE61E5868BD00D3E11B /* IGListCollectionContext+ASDK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "IGListCollectionContext+ASDK.swift"; sourceTree = ""; }; + 427F7FEC1E5872D200D3E11B /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; + DA53F83B08FF5735C4EAA6A5 /* Pods-RepoSearcher.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RepoSearcher.release.xcconfig"; path = "Pods/Target Support Files/Pods-RepoSearcher/Pods-RepoSearcher.release.xcconfig"; sourceTree = ""; }; + DD4426BE878430E4E8B66198 /* Pods_RepoSearcher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RepoSearcher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E925D85286FDB929874729EE /* Pods-RepoSearcher.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RepoSearcher.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RepoSearcher/Pods-RepoSearcher.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 427F7FC31E58519300D3E11B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E222079F2736F3FCAE57814E /* Pods_RepoSearcher.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 12D677BC1212718498BBD9BF /* Frameworks */ = { + isa = PBXGroup; + children = ( + DD4426BE878430E4E8B66198 /* Pods_RepoSearcher.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 427F7FBD1E58519300D3E11B = { + isa = PBXGroup; + children = ( + 427F7FC81E58519300D3E11B /* RepoSearcher */, + 427F7FC71E58519300D3E11B /* Products */, + 427F7FE91E5869F500D3E11B /* Resources */, + 7EEB49B1AF149712F6D01B0B /* Pods */, + 12D677BC1212718498BBD9BF /* Frameworks */, + ); + sourceTree = ""; + }; + 427F7FC71E58519300D3E11B /* Products */ = { + isa = PBXGroup; + children = ( + 427F7FC61E58519300D3E11B /* RepoSearcher.app */, + ); + name = Products; + sourceTree = ""; + }; + 427F7FC81E58519300D3E11B /* RepoSearcher */ = { + isa = PBXGroup; + children = ( + 427F7FC91E58519300D3E11B /* AppDelegate.swift */, + 427F7FE81E5869EB00D3E11B /* View Controllers */, + 427F7FE41E58664700D3E11B /* Section Controller */, + 427F7FE31E58664000D3E11B /* Nodes */, + 427F7FE51E58666400D3E11B /* Extensions */, + ); + path = RepoSearcher; + sourceTree = ""; + }; + 427F7FE31E58664000D3E11B /* Nodes */ = { + isa = PBXGroup; + children = ( + 427F7FDF1E58627B00D3E11B /* SearchNode.swift */, + ); + name = Nodes; + sourceTree = ""; + }; + 427F7FE41E58664700D3E11B /* Section Controller */ = { + isa = PBXGroup; + children = ( + 427F7FDB1E58558C00D3E11B /* LabelSectionController.swift */, + 427F7FDD1E58626A00D3E11B /* SearchSectionController.swift */, + ); + name = "Section Controller"; + sourceTree = ""; + }; + 427F7FE51E58666400D3E11B /* Extensions */ = { + isa = PBXGroup; + children = ( + 427F7FE11E58659600D3E11B /* NSObject+IGListDiffable.swift */, + 427F7FE61E5868BD00D3E11B /* IGListCollectionContext+ASDK.swift */, + ); + name = Extensions; + sourceTree = ""; + }; + 427F7FE81E5869EB00D3E11B /* View Controllers */ = { + isa = PBXGroup; + children = ( + 427F7FCB1E58519300D3E11B /* SearchViewController.swift */, + ); + name = "View Controllers"; + sourceTree = ""; + }; + 427F7FE91E5869F500D3E11B /* Resources */ = { + isa = PBXGroup; + children = ( + 427F7FD01E58519300D3E11B /* Assets.xcassets */, + 427F7FD51E58519300D3E11B /* Info.plist */, + 427F7FEC1E5872D200D3E11B /* Launch Screen.storyboard */, + ); + name = Resources; + path = RepoSearcher; + sourceTree = ""; + }; + 7EEB49B1AF149712F6D01B0B /* Pods */ = { + isa = PBXGroup; + children = ( + E925D85286FDB929874729EE /* Pods-RepoSearcher.debug.xcconfig */, + DA53F83B08FF5735C4EAA6A5 /* Pods-RepoSearcher.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 427F7FC51E58519300D3E11B /* RepoSearcher */ = { + isa = PBXNativeTarget; + buildConfigurationList = 427F7FD81E58519300D3E11B /* Build configuration list for PBXNativeTarget "RepoSearcher" */; + buildPhases = ( + DF4AE1C3A409D227D336F673 /* [CP] Check Pods Manifest.lock */, + 427F7FC21E58519300D3E11B /* Sources */, + 427F7FC31E58519300D3E11B /* Frameworks */, + 427F7FC41E58519300D3E11B /* Resources */, + D44B2BB927A2C0B1D632AB44 /* [CP] Embed Pods Frameworks */, + D6DD975D2C366D8DF2A87634 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = RepoSearcher; + productName = RepoSearcher; + productReference = 427F7FC61E58519300D3E11B /* RepoSearcher.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 427F7FBE1E58519300D3E11B /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = "Marvin Nazari"; + TargetAttributes = { + 427F7FC51E58519300D3E11B = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 427F7FC11E58519300D3E11B /* Build configuration list for PBXProject "RepoSearcher" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 427F7FBD1E58519300D3E11B; + productRefGroup = 427F7FC71E58519300D3E11B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 427F7FC51E58519300D3E11B /* RepoSearcher */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 427F7FC41E58519300D3E11B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 427F7FED1E5872D200D3E11B /* Launch Screen.storyboard in Resources */, + 427F7FD11E58519300D3E11B /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + D44B2BB927A2C0B1D632AB44 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RepoSearcher/Pods-RepoSearcher-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + D6DD975D2C366D8DF2A87634 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RepoSearcher/Pods-RepoSearcher-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + DF4AE1C3A409D227D336F673 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 427F7FC21E58519300D3E11B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 427F7FE21E58659600D3E11B /* NSObject+IGListDiffable.swift in Sources */, + 427F7FCC1E58519300D3E11B /* SearchViewController.swift in Sources */, + 427F7FE01E58627B00D3E11B /* SearchNode.swift in Sources */, + 427F7FE71E5868BD00D3E11B /* IGListCollectionContext+ASDK.swift in Sources */, + 427F7FCA1E58519300D3E11B /* AppDelegate.swift in Sources */, + 427F7FDC1E58558C00D3E11B /* LabelSectionController.swift in Sources */, + 427F7FDE1E58626A00D3E11B /* SearchSectionController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 427F7FD61E58519300D3E11B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 427F7FD71E58519300D3E11B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 427F7FD91E58519300D3E11B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E925D85286FDB929874729EE /* Pods-RepoSearcher.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = RepoSearcher/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = wavio.RepoSearcher; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 427F7FDA1E58519300D3E11B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DA53F83B08FF5735C4EAA6A5 /* Pods-RepoSearcher.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + INFOPLIST_FILE = RepoSearcher/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = wavio.RepoSearcher; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 427F7FC11E58519300D3E11B /* Build configuration list for PBXProject "RepoSearcher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 427F7FD61E58519300D3E11B /* Debug */, + 427F7FD71E58519300D3E11B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 427F7FD81E58519300D3E11B /* Build configuration list for PBXNativeTarget "RepoSearcher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 427F7FD91E58519300D3E11B /* Debug */, + 427F7FDA1E58519300D3E11B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 427F7FBE1E58519300D3E11B /* Project object */; +} diff --git a/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..bd13674404 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/examples_extra/RepoSearcher/RepoSearcher.xcworkspace/contents.xcworkspacedata b/examples_extra/RepoSearcher/RepoSearcher.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..c9f12e24dd --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/examples_extra/RepoSearcher/RepoSearcher/AppDelegate.swift b/examples_extra/RepoSearcher/RepoSearcher/AppDelegate.swift new file mode 100644 index 0000000000..d9e8b19907 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/AppDelegate.swift @@ -0,0 +1,27 @@ +// +// AppDelegate.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? = { + let window = UIWindow(frame: UIScreen.main.bounds) + window.backgroundColor = .white + return window + }() + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + window?.rootViewController = UINavigationController(rootViewController: SearchViewController()) + window?.makeKeyAndVisible() + + return true + } +} + diff --git a/examples_extra/RepoSearcher/RepoSearcher/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples_extra/RepoSearcher/RepoSearcher/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..36d2c80d88 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples_extra/RepoSearcher/RepoSearcher/IGListCollectionContext+ASDK.swift b/examples_extra/RepoSearcher/RepoSearcher/IGListCollectionContext+ASDK.swift new file mode 100644 index 0000000000..b1c623b651 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/IGListCollectionContext+ASDK.swift @@ -0,0 +1,17 @@ +// +// IGListCollectionContext+ASDK.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import Foundation +import IGListKit +import AsyncDisplayKit + +extension IGListCollectionContext { + func nodeForItem(at index: Int, sectionController: IGListSectionController) -> ASCellNode? { + return (cellForItem(at: index, sectionController: sectionController) as? _ASCollectionViewCell)?.node + } +} diff --git a/examples_extra/RepoSearcher/RepoSearcher/Info.plist b/examples_extra/RepoSearcher/RepoSearcher/Info.plist new file mode 100644 index 0000000000..0f3cc77a42 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + Launch Screen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/examples_extra/RepoSearcher/RepoSearcher/LabelSectionController.swift b/examples_extra/RepoSearcher/RepoSearcher/LabelSectionController.swift new file mode 100644 index 0000000000..040634c934 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/LabelSectionController.swift @@ -0,0 +1,44 @@ +// +// LabelSectionController.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import Foundation +import AsyncDisplayKit +import IGListKit + +final class LabelSectionController: IGListSectionController, IGListSectionType, ASSectionController { + var object: String? + + func nodeBlockForItem(at index: Int) -> ASCellNodeBlock { + let text = object ?? "" + return { + let node = ASTextCellNode() + node.text = text + return node + } + } + + func numberOfItems() -> Int { + return 1 + } + + func didUpdate(to object: Any) { + self.object = String(describing: object) + } + + func didSelectItem(at index: Int) {} + + //ASDK Replacement + func sizeForItem(at index: Int) -> CGSize { + return ASIGListSectionControllerMethods.sizeForItem(at: index) + } + + func cellForItem(at index: Int) -> UICollectionViewCell { + return ASIGListSectionControllerMethods.cellForItem(at: index, sectionController: self) + } +} + diff --git a/examples_extra/RepoSearcher/RepoSearcher/Launch Screen.storyboard b/examples_extra/RepoSearcher/RepoSearcher/Launch Screen.storyboard new file mode 100644 index 0000000000..b90f693902 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/Launch Screen.storyboard @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples_extra/RepoSearcher/RepoSearcher/NSObject+IGListDiffable.swift b/examples_extra/RepoSearcher/RepoSearcher/NSObject+IGListDiffable.swift new file mode 100644 index 0000000000..176b484300 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/NSObject+IGListDiffable.swift @@ -0,0 +1,18 @@ +// +// NSObject+IGListDiffable.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import IGListKit + +extension NSObject: IGListDiffable { + public func diffIdentifier() -> NSObjectProtocol { + return self + } + public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool { + return isEqual(object) + } +} diff --git a/examples_extra/RepoSearcher/RepoSearcher/SearchNode.swift b/examples_extra/RepoSearcher/RepoSearcher/SearchNode.swift new file mode 100644 index 0000000000..440cc3752e --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/SearchNode.swift @@ -0,0 +1,50 @@ +// +// SearchNode.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import Foundation +import AsyncDisplayKit + +class SearchNode: ASCellNode { + var searchBarNode: SearchBarNode + + init(delegate: UISearchBarDelegate?) { + self.searchBarNode = SearchBarNode(delegate: delegate) + super.init() + automaticallyManagesSubnodes = true + } + + override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec { + return ASInsetLayoutSpec(insets: .zero, child: searchBarNode) + } +} + +final class SearchBarNode: ASDisplayNode { + + weak var delegate: UISearchBarDelegate? + + init(delegate: UISearchBarDelegate?) { + self.delegate = delegate + super.init(viewBlock: { + UISearchBar() + }, didLoad: nil) + style.preferredSize = CGSize(width: UIScreen.main.bounds.width, height: 44) + } + + var searchBar: UISearchBar { + return view as! UISearchBar + } + + override func didLoad() { + super.didLoad() + searchBar.delegate = delegate + searchBar.searchBarStyle = .minimal + searchBar.tintColor = .black + searchBar.backgroundColor = .white + searchBar.placeholder = "Search" + } +} diff --git a/examples_extra/RepoSearcher/RepoSearcher/SearchSectionController.swift b/examples_extra/RepoSearcher/RepoSearcher/SearchSectionController.swift new file mode 100644 index 0000000000..75738a0c99 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/SearchSectionController.swift @@ -0,0 +1,71 @@ +// +// SearchSectionController.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import AsyncDisplayKit +import IGListKit + +protocol SearchSectionControllerDelegate: class { + func searchSectionController(_ sectionController: SearchSectionController, didChangeText text: String) +} + +final class SearchSectionController: IGListSectionController, IGListSectionType, ASSectionController { + + weak var delegate: SearchSectionControllerDelegate? + + override init() { + super.init() + scrollDelegate = self + } + + func nodeBlockForItem(at index: Int) -> ASCellNodeBlock { + return { [weak self] in + return SearchNode(delegate: self) + } + } + + func numberOfItems() -> Int { + return 1 + } + + func didUpdate(to object: Any) {} + func didSelectItem(at index: Int) {} + + //ASDK Replacement + func sizeForItem(at index: Int) -> CGSize { + return ASIGListSectionControllerMethods.sizeForItem(at: index) + } + + func cellForItem(at index: Int) -> UICollectionViewCell { + return ASIGListSectionControllerMethods.cellForItem(at: index, sectionController: self) + } +} + +extension SearchSectionController: IGListScrollDelegate { + func listAdapter(_ listAdapter: IGListAdapter, didScroll sectionController: IGListSectionController) { + guard let searchNode = collectionContext?.nodeForItem(at: 0, sectionController: self) as? SearchNode else { return } + + let searchBar = searchNode.searchBarNode.searchBar + searchBar.text = "" + searchBar.resignFirstResponder() + } + + func listAdapter(_ listAdapter: IGListAdapter!, willBeginDragging sectionController: IGListSectionController!) {} + func listAdapter(_ listAdapter: IGListAdapter!, didEndDragging sectionController: IGListSectionController!, willDecelerate decelerate: Bool) {} + +} + +extension SearchSectionController: UISearchBarDelegate { + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + delegate?.searchSectionController(self, didChangeText: searchText) + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + delegate?.searchSectionController(self, didChangeText: "") + } +} + diff --git a/examples_extra/RepoSearcher/RepoSearcher/SearchViewController.swift b/examples_extra/RepoSearcher/RepoSearcher/SearchViewController.swift new file mode 100644 index 0000000000..35f1c39134 --- /dev/null +++ b/examples_extra/RepoSearcher/RepoSearcher/SearchViewController.swift @@ -0,0 +1,65 @@ +// +// SearchViewController.swift +// RepoSearcher +// +// Created by Marvin Nazari on 2017-02-18. +// Copyright © 2017 Marvin Nazari. All rights reserved. +// + +import UIKit +import AsyncDisplayKit +import IGListKit + +class SearchToken: NSObject {} + +final class SearchViewController: ASViewController { + + lazy var adapter: IGListAdapter = { + return IGListAdapter(updater: IGListAdapterUpdater(), viewController: self, workingRangeSize: 0) + }() + + let words = ["first", "second", "third", "more", "hi", "others"] + + let searchToken = SearchToken() + var filterString = "" + + init() { + let flowLayout = UICollectionViewFlowLayout() + super.init(node: ASCollectionNode(collectionViewLayout: flowLayout)) + adapter.setASDKCollectionNode(node) + adapter.dataSource = self + title = "Search" + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension SearchViewController: IGListAdapterDataSource { + func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController { + if object is SearchToken { + let section = SearchSectionController() + section.delegate = self + return section + } + return LabelSectionController() + } + + func emptyView(for listAdapter: IGListAdapter) -> UIView? { + // emptyView dosent work in this secenario, there is always one section (searchbar) present in collection + return nil + } + + func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] { + guard filterString != "" else { return [searchToken] + words.map { $0 as IGListDiffable } } + return [searchToken] + words.filter { $0.lowercased().contains(filterString.lowercased()) }.map { $0 as IGListDiffable } + } +} + +extension SearchViewController: SearchSectionControllerDelegate { + func searchSectionController(_ sectionController: SearchSectionController, didChangeText text: String) { + filterString = text + adapter.performUpdates(animated: true, completion: nil) + } +} diff --git a/inferScript.sh b/inferScript.sh deleted file mode 100755 index 81919c1e81..0000000000 --- a/inferScript.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if ! [ -x "$(command -v infer)" ]; then - echo "infer not found" - echo "Install infer with homebrew: brew install infer" -else - infer --continue --reactive -- xcodebuild build -workspace AsyncDisplayKit.xcworkspace -scheme "AsyncDisplayKit-iOS" -configuration Debug -sdk iphonesimulator9.3 -fi diff --git a/plans/section-infos-api/Overview.md b/plans/section-infos-api/Overview.md deleted file mode 100644 index 485c86d587..0000000000 --- a/plans/section-infos-api/Overview.md +++ /dev/null @@ -1,34 +0,0 @@ -# Overview - -There is an established pattern where UICollectionViewLayout talks directly to the collection view's delegate to get additional layout info e.g. the size of a header in a given section. - -This pattern is established by Apple's flow layout, and it is used by Pinterest and I'm sure others. It is dangerous when used with ASDK because we update asynchronously, so -for instance if you delete a section from your data source, the layout won't find out until later and in the meantime it may ask the delegate about a section that doesn't exist! - -The solution is to capture this kind of information from the data source immediately when a section is inserted, and make it available to the layout as we update the UICollectionView so that everyone is on the same page. - -Enter: ASSectionUserInfo - -Internally, we use a private object ASCollectionSection to represent one version of a section of items and supplementaries. If the user wants, they can provide us with an ASSectionUserInfo object to accompany the section, which will be read synchronously when the section is inserted. - -## Usage During Layout - -The collection view will make these section infos available in the same way that finished nodes are currently available. - -#### [Sequence Diagram:][diag-layout-usage] - -![][image-layout-usage] - -## Creation When Inserting Sections - -The section infos for any inserted/reloaded sections are queried synchronously, before the node blocks for their items. The top part of this diagram is the same as the current behavior but I think it's useful info =) - -#### [Sequence Diagram:][diag-inserting-sections] - -![][image-inserting-sections] - - -[diag-inserting-sections]: https://www.websequencediagrams.com/?lz=dGl0bGUgSW5zZXJ0aW5nL1JlbG9hZGluZyBTZWN0aW9ucwoKRGF0YSBTb3VyY2UtPkNWOiBpACoFABkIQXRJbmRleGVzOgpDVi0-Q2hhbmdlU2V0IAAzBUNvbnRyb2xsZXIAHRsAURFlbmRVcGRhdGVzADQgAB4MAGIYLT4AehFiZWdpbgAFNACBYxlsb29wIGZvciBlYWNoIHMAgjgGIGluZGV4CiAgIACBAhJDVjoAHwhJbmZvAIJABzoAKAVDVgCBLQcAgnEGAA8aAIMQDACDFwZSZXR1cm4gaWQ8QVMAgz0HVXNlckluZm8-AFQIAIF-EwAWIQCBUg5pdGVtIGluAIFgCACBXQUAgU0Zbm9kZUJsb2NrAIQkB1BhdGgAgWIGAIFXFQARHgCBWRlub2RlIGJsb2NrAE8MAIFRGgAhD2VuZAplbmQAhBktAIUcCwCEYxAAhW0cAIUPGwCGWwYKAIMaCgCDeQgKCg&s=napkin -[image-inserting-sections]: https://www.websequencediagrams.com/cgi-bin/cdraw?lz=dGl0bGUgSW5zZXJ0aW5nL1JlbG9hZGluZyBTZWN0aW9ucwoKRGF0YSBTb3VyY2UtPkNWOiBpACoFABkIQXRJbmRleGVzOgpDVi0-Q2hhbmdlU2V0IAAzBUNvbnRyb2xsZXIAHRsAURFlbmRVcGRhdGVzADQgAB4MAGIYLT4AehFiZWdpbgAFNACBYxlsb29wIGZvciBlYWNoIHMAgjgGIGluZGV4CiAgIACBAhJDVjoAHwhJbmZvAIJABzoAKAVDVgCBLQcAgnEGAA8aAIMQDACDFwZSZXR1cm4gaWQ8QVMAgz0HVXNlckluZm8-AFQIAIF-EwAWIQCBUg5pdGVtIGluAIFgCACBXQUAgU0Zbm9kZUJsb2NrAIQkB1BhdGgAgWIGAIFXFQARHgCBWRlub2RlIGJsb2NrAE8MAIFRGgAhD2VuZAplbmQAhBktAIUcCwCEYxAAhW0cAIUPGwCGWwYKAIMaCgCDeQgKCg&s=napkin -[image-layout-usage]: https://www.websequencediagrams.com/cgi-bin/cdraw?lz=dGl0bGUgcHJlcGFyZUxheW91dCAtIHNlY3Rpb24gbWV0cmljcwoKQ1YtPgAYBjoAHw4KbG9vcCBmb3IgZWFjaAAxCAogICAgCiAgICAATQYtPkNWOgBOCEluZm9BdEluZGV4OgAkBUNWLQBUClJldHVybiBQSU1hc29ucnlTACsKCgBFDQAOFDogY29sdW1uQ291bnQAgQAFADQUAFwSACYQAEYed2lkdGhPZkMAZgUAgT0NAEgmADsFAIIRDQCCUwhVc2UAgmsJZW5kCgoAgjgHAII6BihPSykK&s=napkin -[diag-layout-usage]: https://www.websequencediagrams.com/?lz=dGl0bGUgcHJlcGFyZUxheW91dCAtIHNlY3Rpb24gbWV0cmljcwoKQ1YtPgAYBjoAHw4KbG9vcCBmb3IgZWFjaAAxCAogICAgCiAgICAATQYtPkNWOgBOCEluZm9BdEluZGV4OgAkBUNWLQBUClJldHVybiBQSU1hc29ucnlTACsKCgBFDQAOFDogY29sdW1uQ291bnQAgQAFADQUAFwSACYQAEYed2lkdGhPZkMAZgUAgT0NAEgmADsFAIIRDQCCUwhVc2UAgmsJZW5kCgoAgjgHAII6BihPSykK&s=napkin